gdharness
A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.
npm · fingerprint ff0fc5331c37273ceef0c9e6 · repository · RSS
What changed
Changed 2026-10-18 10:47 UTC
6 descriptions rewritten, 5 schema or annotation changes with the description left byte-identical.
--- pinned/editor_run +++ observed/editor_run { "annotations": { "destructiveHint": true, + "mark": { + "description": "profile: answer as usual and set the run's mark at these totals, so a later profile with since: \"mark\" answers for one action, a press or a commit, apart from the boot and everything before it. The mark is kept beside the run's profile, so a server that replaces this one reads it too, and setting it again moves it. Given with since, the answer counts from the mark before it moves, which times one span after another. Default false. Only for op profile.", + "type": "boolean" + }, "op": { "description": "What to do.", "enum": [ + "since": { + "description": "profile: \"mark\" answers only what was added after the mark that mark: true set in this run: per function the calls and self and total milliseconds since it, with the functions that added no call left out, frames and coveredMs since it, and callersOf read off the same difference. Refused when no mark was set in this run, or when the profile holds less than it did at the mark, which is a profiler started again since. Only for op profile.", + "enum": [ + "mark" + ], + "type": "string" + }, "timeoutMs": { "description": "check: how long to give the boot before it is called hung. Default 60000. wait: how long to wait for the run to end before answering anyway. Default 600000. Only for ops check, wait.", "minimum": 1,
--- pinned/project_import +++ observed/project_import { "annotations": { "destructiveHint": true, - "description": "The import pipeline: what needs importing, how a resource is imported, reimports, UIDs, and the global class list the editor and the engine read. Operations: status: which resources are outdated or failed, or one resource with resourcePath, each with its reason. Judged by content with the checks the editor makes before it reimports: a sidecar changed since its import, as setting an option without a reimport leaves it; a sidecar with no uid or written for another file; an output under .godot/imported that is gone (missing_outputs) or changed; a source whose hash differs from the one its import recorded, or with no record. Beyond the editor, a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without; a scene whose import runs an import script is not judged on images). failed is an import that failed, which the editor never tries again on its own. Without resourcePath, a sidecar whose source is gone is listed as missing_source; options (needs resourcePath): the import options of one resource; set_options (needs resourcePath, options): change import options in the sidecar, then apply them with a reimport of that resource; reimport: reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs; uid (needs resourcePath): the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header; refresh_uids: import the project so every script and shader has its .uid sidecar, and name under uidsCreated the ones this made and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched; refresh_classes: rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so \"added: []\" means the file was already right rather than that nothing is wrong. A class the cache held after the last rebuild and had lost when this one began is named under lostSinceLastRebuild: the editor wrote the file without it in between, on a save or a scan, and will again until restarted.", + "description": "The import pipeline: what needs importing, how a resource is imported, reimports, UIDs, and the global class list the editor and the engine read. Operations: status: which resources are outdated or failed, or one resource with resourcePath, each with its reason. Judged by content with the checks the editor makes before it reimports: a sidecar changed since its import, as setting an option without a reimport leaves it; a sidecar with no uid or written for another file; an output under .godot/imported that is gone (missing_outputs) or changed; a source whose hash differs from the one its import recorded, or with no record. Beyond the editor, a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without; a scene whose import runs an import script is not judged on images). failed is an import that failed, which the editor never tries again on its own. Without resourcePath, a sidecar whose source is gone is listed as missing_source; options (needs resourcePath): the import options of one resource; set_options (needs resourcePath, options): change import options in the sidecar, then apply them with a reimport of that resource; reimport: reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. When nothing needs reimporting, nothing runs, and the answer has the two lists empty, a note saying so, and no via. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs; uid (needs resourcePath): the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header; refresh_uids: import the project so every script and shader has its .uid sidecar, and name under uidsCreated the ones this made and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched; refresh_classes: rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so \"added: []\" means the file was already right rather than that nothing is wrong. A class the cache held after the last rebuild and had lost when this one began is named under lostSinceLastRebuild: the editor wrote the file without it in between, on a save or a scan, and will again until restarted.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/project_info +++ observed/project_info { "annotations": { "openWorldHint": false, - "description": "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, with optional sections on top.", + "description": "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, other counting every remaining file, leaving out the addons gdharness installed and any folder holding a .gdignore, with optional sections on top.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/project_test +++ observed/project_test { "annotations": { "destructiveHint": false, - "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, its reports under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. Identical reports fold into one with times, at most twenty different ones are kept (reportsLeftOut counts the rest), and past about a megabyte the largest cases keep only their first message (failedWithoutDetail). failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", + "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, its reports under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. Identical reports fold into one with times, at most twenty different ones are kept (reportsLeftOut counts the rest), and past about a megabyte the largest cases keep only their first message (failedWithoutDetail). failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted, not listed, and an engine message keeps the frames above gdUnit4, not the twenty inside it; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, away from a player's saves; on macOS the engine reads user:// off HOME, so it writes where the player keeps theirs, and savesNote says so. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. An autoload that does not compile stops the run at once, its errors listed the same way. A client sending a progress token is told as each suite finishes, and at least every twenty seconds, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path.", + "description": "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path, unless it ended repeating one script error, which the verdict names instead. A run whose autoload did not compile is stopped as soon as the engine says so, and names the script that did not under scriptErrors.", "minimum": 1, "type": "integer" }
--- pinned/resource_edit +++ observed/resource_edit { "annotations": { "destructiveHint": true, - "description": "Theme ops: the Control class, such as \"Button\". Only for ops set_theme_color, set_theme_font_size.", + "description": "Theme ops: the theme type, a Control or Window class such as \"Button\", or the name of a type variation, which can be any name. A name that is no such class is set as a variation and answered with a note saying so. Only for ops set_theme_color, set_theme_font_size.", "type": "string" }, "fontSizeName": { - "description": "create: the resource class, such as \"PhysicsMaterial\" or \"StandardMaterial3D\". Only for op create.", + "description": "create: the resource class, such as \"PhysicsMaterial\" or \"StandardMaterial3D\", or a class_name a project script declares, which the resource is made as with that script attached. Only for op create.", "type": "string" }, "script": { - "description": "set_theme_font_size: pixels. Only for op set_theme_font_size.", + "description": "set_theme_font_size: pixels, a whole number of 1 or more. Only for op set_theme_font_size.", "type": "number" }, "sources": {
--- pinned/runtime_input +++ observed/runtime_input { "annotations": { "destructiveHint": false, - "description": "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when another control says exactly the words (hidden, off the screen, covered, or on screen taking clicks without being a button), since it would stand in for that one, and when it lets the pointer through to what is under it; index presses it anyway. Words found only inside longer words (\"out\" in \"Scout\") rank after every other match and are refused unless index is given; words starting a longer word (\"Recruit\" in \"Recruits\") count. An empty field says its placeholder, and a control with no text of its own, a picture on a button, says its accessibility name. An item of an open menu is drawn by the menu rather than held as a control, so a click finds none, and the refusal names the menu and the choose call that picks it. Only for op click.", + "description": "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when another control says exactly the words (hidden, off the screen, covered, or on screen taking clicks without being a button), since it would stand in for that one, and when it lets the pointer through to what is under it; index presses it anyway. Words found only inside longer words (\"out\" in \"Scout\") rank after every other match and are refused unless index is given; words starting a longer word (\"Recruit\" in \"Recruits\") count. An empty field says its placeholder, and a control with no text of its own, a picture on a button, says its accessibility name. A tab, a list entry, a tree row and a menu bar title each count as a match of their own and are pressed where they are drawn, after their control scrolls them into view; a disabled one is refused. An item of an open menu is drawn by the menu rather than held as a control, so a click finds none, and the refusal names the menu and the choose call that picks it. Only for op click.", "type": "string" }, "shift": {
--- pinned/runtime_inspect +++ observed/runtime_inspect { "annotations": { "openWorldHint": false, - "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: \"size:x\", \"global_position:y\", \"modulate:a\", \"global_transform:origin:z\"; a name the value does not have is refused, naming the ones it has. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead. Only for ops property, find.", + "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A key holding a colon, a bracket or a quote is written in double quotes, 'stock:\"base:dew\"', as a refusal listing the keys writes it. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: \"size:x\", \"global_position:y\", \"modulate:a\", \"global_transform:origin:z\"; a name the value does not have is refused, naming the ones it has. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead. Only for ops property, find.", "type": "string" }, "says": {
--- pinned/scene_animation +++ observed/scene_animation { "annotations": { "destructiveHint": false, - "description": "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. read and read_states need no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without running the scene's scripts. The ops that write need the editor connected. Operations: read (needs playerNodePath): the animations of an AnimationPlayer, or one with its tracks; read_states (needs animTreePath): the states and transitions of an AnimationTree state machine; create (needs playerNodePath, animationName): a new animation in an AnimationPlayer; add_track (needs playerNodePath, animationName, track): a property or method track with keyframes; add_state (needs animTreePath, stateName, animationName): a state playing an animation, in an AnimationTree; connect_states (needs animTreePath, fromState, toState): a transition between two states.", + "description": "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. read and read_states need no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without running the scene's scripts. The ops that write go through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: read (needs playerNodePath): the animations of an AnimationPlayer, or one with its tracks; read_states (needs animTreePath): the states and transitions of an AnimationTree state machine; create (needs playerNodePath, animationName): a new animation in an AnimationPlayer; add_track (needs playerNodePath, animationName, track): a property or method track with keyframes; add_state (needs animTreePath, stateName, animationName): a state playing an animation, in an AnimationTree; connect_states (needs animTreePath, fromState, toState): a transition between two states.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "The animation in the player. read: the one to answer with whole; without it, read lists them. add_state: the one that state plays. Only for ops read, create, add_track, add_state.", + "description": "The animation in the player. read: the one to answer with whole, its tracks a list; without it, read lists them, each with the number of its tracks under tracks. add_state: the one that state plays. Only for ops read, create, add_track, add_state.", "type": "string" }, "fromState": { - "description": "For method tracks: the method to call.", + "description": "For method tracks: the method to call. The answer's methodFound says whether the target node has it; a method it lacks is still added, since it may be written next, and until it exists each call the track makes fails.", "type": "string" }, "nodePath": {
--- pinned/scene_create +++ observed/scene_create { "annotations": { "destructiveHint": true, - "description": "create: the root node class. Default Node. Only for op create.", + "description": "create: the root node class, an engine class or a class_name a project script declares, which the root is made as with that script attached. Default Node. Only for op create.", "type": "string" }, "scenePath": {
--- pinned/scene_node +++ observed/scene_node { "annotations": { "destructiveHint": true, - "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {\"name\": \"Boss\"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: add (needs nodeName): add a node of any class, or an instance of a scene; get (needs nodePath): read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent, or to another place among its siblings; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", + "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type or script class can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {\"name\": \"Boss\"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: add (needs nodeName): add a node of any class, or an instance of a scene; get (needs nodePath): read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent, or to another place among its siblings; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "add: the node class, such as \"CharacterBody2D\". Give this or instancePath. Only for op add.", + "description": "add: the node class, such as \"CharacterBody2D\", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath. Only for op add.", "type": "string" }, "op": {
--- pinned/scene_signal +++ observed/scene_signal { "annotations": { "destructiveHint": true, - "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", + "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. A connect to a method the target does not have is still written, as the editor's own dialog allows, since the method may be written next; methodFound false says so, and until the method exists the signal fails each time it is emitted. list marks editable false on a connection an instanced scene makes inside itself, which only that scene can change. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", "inputSchema": { "additionalProperties": false, "properties": {
Changed 2026-10-17 10:29 UTC
14 descriptions rewritten, 4 schema or annotation changes with the description left byte-identical.
--- pinned/editor_classes +++ observed/editor_classes { "annotations": { "openWorldHint": false, - "description": "query: limit to one family.", + "description": "query: limit to one family. Only for op query.", "enum": [ "node", "node2d", - "description": "info, inheritance: the class.", + "description": "info, inheritance: the class. Only for ops info, inheritance.", "type": "string" }, "filter": { - "description": "query: a substring of the class name.", + "description": "query: a substring of the class name. Only for op query.", "type": "string" }, "includeInherited": { - "description": "info: include inherited members. Default false.", + "description": "info: include inherited members. Default false. Only for op info.", "type": "boolean" }, "instantiableOnly": { - "description": "query: leave out abstract classes. Default false.", + "description": "query: leave out abstract classes. Default false. Only for op query.", "type": "boolean" }, "member": {
--- pinned/project_dependencies +++ observed/project_dependencies { "annotations": { "openWorldHint": false, - "description": "forward: what this resource loads, with cycles reported. reverse: every file that refers to it and how, a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name. Each reference carries a kind, and two of them are mentions rather than uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs, and comment is one in ordinary prose, which it would only make stale. summary.in_code is the count with both of those left out, which is the number to read when the question is whether anything still uses this. Default forward.", + "description": "forward: what this resource loads, with cycles reported, a script's dependencies including every script whose class_name it uses, marked by_class with the name. summary.total_dependencies counts each file once however many places load it, and total_references counts the entries in the tree. reverse: every file that refers to it and how, a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name. Each reference carries a kind: ext_resource, extends, preload, load or path for a reference by path; setting, plugin, export_preset and import for one in project.godot, a plugin.cfg, export_presets.cfg or an import sidecar; class_name for a use of the declared name, the declaration itself being the script asked about. Two kinds are mentions rather than uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs, and comment is one in ordinary prose, which it would only make stale. summary.in_code is the count with both of those left out, which is the number to read when the question is whether anything still uses this. Default forward.", "enum": [ "forward", "reverse"
--- pinned/project_export +++ observed/project_export { "annotations": { "destructiveHint": true, - "description": "Export presets and exports. Operations: list: the presets in export_presets.cfg; run (needs preset, outputPath): export with a preset.", + "description": "Export presets and exports. add_preset writes a preset to export_presets.cfg with the keys the editor's Project > Export writes for a new one, every platform option left at its default, and makes it the platform's runnable preset when the platform has none. It answers where this engine's export templates belong and whether the preset's platform has them, since an export needs them; list says the same for every preset. An editor open on the project does not see a preset added this way until it restarts. run names, as alsoWritten, what else the export wrote beside its output, such as the .pck a Windows or Linux build cannot start without. Operations: list: the presets in export_presets.cfg; run (needs preset, outputPath): export with a preset; add_preset (needs preset, platform): add an export preset to export_presets.cfg.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "run: a debug export. Default false.", + "description": "run: a debug export. Default false. Only for op run.", "type": "boolean" }, "op": { - "run" + "run", + "add_preset" ], "type": "string" }, - "description": "Where the export is written, inside the project. The directory is created if it is not there: Godot's command-line exporter refuses a missing one with \"The given export path doesn't exist\", which reads as a wrong path in the preset.", + "description": "Where the export is written, inside the project. The directory is created if it is not there: Godot's command-line exporter refuses a missing one with \"The given export path doesn't exist\", which reads as a wrong path in the preset. add_preset: the path the preset exports to from the editor, optional. Only for ops run, add_preset.", "type": "string" }, + "platform": { + "description": "add_preset: the platform the preset exports for. Only for op add_preset.", + "enum": [ + "Windows Desktop", + "Linux", + "macOS", + "Android", + "iOS", + "Web" + ], + "type": "string" + }, "preset": { - "description": "Preset name from export_presets.cfg.", + "description": "Preset name from export_presets.cfg. add_preset: the name of the new one. Only for ops run, add_preset.", "type": "string" }, "projectPath": {
--- pinned/project_import +++ observed/project_import { "annotations": { "destructiveHint": true, - "description": "reimport: reimport even what is current. Default false.", + "description": "reimport: reimport even what is current. Default false. Only for op reimport.", "type": "boolean" }, "includeUpToDate": { - "description": "status: list resources that are current as well. Default false.", + "description": "status: list resources that are current as well. Default false. Only for op status.", "type": "boolean" }, "op": { - "description": "set_options: import options keyed as the .import file spells them, {\"compress/mode\": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {\"opentype_features\": {\"liga\": 0}} turns ligatures off. Both are written as the numbers the engine reads.", + "description": "set_options: import options keyed as the .import file spells them, {\"compress/mode\": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {\"opentype_features\": {\"liga\": 0}} turns ligatures off. Both are written as the numbers the engine reads. Only for op set_options.", "type": "object" }, "projectPath": { - "description": "set_options: reimport afterwards, answered under reimport as project_import reimport answers. Default true.", + "description": "set_options: reimport afterwards, answered under reimport as project_import reimport answers. Default true. Only for op set_options.", "type": "boolean" }, "resourcePath": { - "description": "File inside the project, such as \"sprites/hero.png\" or \"materials/steel.tres\".", + "description": "File inside the project, such as \"sprites/hero.png\" or \"materials/steel.tres\". Only for ops status, options, set_options, uid, reimport.", "type": "string" }, "timeoutMs": { - "description": "reimport, set_options: how long to wait for the editor to finish reimporting. Default 300000.", + "description": "reimport, set_options: how long to wait for the editor to finish reimporting. Default 300000. Only for ops reimport, set_options.", "minimum": 1, "type": "integer" }
--- pinned/project_settings +++ observed/project_settings { "annotations": { "destructiveHint": true, - "description": "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. A set of the value the setting already holds, in the same type, writes nothing and answers unchanged true and saved false. Every write keeps the sections and keys of project.godot in the order the file had them, so a change touches only its own lines. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot. Each setting under prefix carries at_default too, so a family read separates what the project sets from the engine's defaults; it is absent for a setting the engine registers no default for, such as an input action the project added, which only the project can have set. Operations: get: read one setting, or every setting under prefix; set (needs setting, value): write one setting; add_autoload (needs name, path): register an autoload singleton; remove_autoload (needs name): unregister an autoload; set_main_scene (needs scenePath): choose the scene the game starts in; add_input_action (needs actionName, events): register an input action and its events; remove_input_action (needs actionName): remove an input action the project defines; enable_plugin (needs pluginName): enable an addon; disable_plugin (needs pluginName): disable an addon; add_audio_bus (needs busName): add an audio bus; remove_audio_bus (needs busName): remove an audio bus, sending what sent to it on to where it sent; set_audio_bus_effect (needs busIndex, effectIndex, effectType): put an effect in a slot on a bus; set_audio_bus_volume (needs busIndex, volumeDb): set a bus volume.", + "description": "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. add_input_action on an action that exists, one of the engine's ui_ actions included, replaces its events and deadzone, and answers was_new false with what it held under old_events and old_deadzone. A set of the value the setting already holds, in the same type, writes nothing and answers unchanged true and saved false. Every write keeps the sections and keys of project.godot in the order the file had them, so a change moves no other line; the save is the engine's, and like the editor's it writes Godot's header and config/features into a file that lacks them. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot. Each setting under prefix carries at_default too, so a family read separates what the project sets from the engine's defaults; it is absent for a setting the engine registers no default for, such as an input action the project added, which only the project can have set. Operations: get: read one setting, or every setting under prefix; set (needs setting, value): write one setting; add_autoload (needs name, path): register an autoload singleton; remove_autoload (needs name): unregister an autoload; set_main_scene (needs scenePath): choose the scene the game starts in; add_input_action (needs actionName, events): register an input action and its events, or rebind one that exists; remove_input_action (needs actionName): remove an input action the project defines; enable_plugin (needs pluginName): enable an addon; disable_plugin (needs pluginName): disable an addon; add_audio_bus (needs busName): add an audio bus; remove_audio_bus (needs busName): remove an audio bus, sending what sent to it on to where it sent; set_audio_bus_effect (needs busIndex, effectIndex, effectType): put an effect in a slot on a bus; set_audio_bus_volume (needs busIndex, volumeDb): set a bus volume.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/project_test +++ observed/project_test { "annotations": { "destructiveHint": false, - "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, every report under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", + "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, its reports under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. Identical reports fold into one with times, at most twenty different ones are kept (reportsLeftOut counts the rest), and past about a megabyte the largest cases keep only their first message (failedWithoutDetail). failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/resource_edit +++ observed/resource_edit { "annotations": { "destructiveHint": true, - "description": "Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as \"Button/colors/font_color\". move takes any file of the project, a script or an image as much as a resource, or a directory, and works from the files, so it needs no editor; one serving the project is brought up to it as after a move in its own FileSystem dock, its open scenes and cached resources named for the new paths and the project settings the move rewrote taken from the file. read needs no editor either: with none serving the project, a headless engine reads the file. The ops that write a resource need the editor connected. Operations: read: the properties a resource file sets; create (needs resourceType): a new resource of any class; modify (needs properties): set properties on an existing resource; create_shader (needs shaderType): a .gdshader file; create_tileset (needs sources): a TileSet from texture atlases; set_theme_color (needs controlType, colorName, color): a colour in a Theme; set_theme_font_size (needs controlType, fontSizeName, size): a font size in a Theme; move (needs newPath): move or rename a file or a directory, and every path in the project naming it.", + "description": "Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as \"Button/colors/font_color\". move takes any file of the project, a script or an image as much as a resource, or a directory, and works from the files, so it needs no editor; one serving the project is brought up to it as after a move in its own FileSystem dock, its open scenes and cached resources named for the new paths and the project settings the move rewrote taken from the file. read needs no editor either: with none serving the project, a headless engine reads the file. The ops that write a resource go through the editor when one serves the project; with none, a headless engine runs the tool the editor would and the answer says so under writtenBy. That engine runs the script of the resource, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before the save, and a resource where that cannot be done is refused and the file left as it was. Operations: read: the properties a resource file sets; create (needs resourceType): a new resource of any class; modify (needs properties): set properties on an existing resource; create_shader (needs shaderType): a .gdshader file; create_tileset (needs sources): a TileSet from texture atlases; set_theme_color (needs controlType, colorName, color): a colour in a Theme; set_theme_font_size (needs controlType, fontSizeName, size): a font size in a Theme; move (needs newPath): move or rename a file or a directory, and every path in the project naming it.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: \"position:x\", \"modulate:a\", \"region_rect:size:y\"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds. Only for ops create, modify.", + "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: \"position:x\", \"modulate:a\", \"region_rect:size:y\"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds. A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {\"_type\": \"int\", \"value\": \"9007199254740993\"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused. Only for ops create, modify.", "type": "object" }, "resourcePath": { - "description": "File inside the project, such as \"sprites/hero.png\" or \"materials/steel.tres\". move: a directory too.", + "description": "File inside the project, such as \"sprites/hero.png\" or \"materials/steel.tres\". move: a directory too. read: also a resource kept inside a scene or resource file, by the path a read answers a property holding it with, such as \"res://hero.tscn::Animation_jump\".", "type": "string" }, "resourceType": {
--- pinned/runtime_input +++ observed/runtime_input { "annotations": { "destructiveHint": false, - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button, mouse motion or drag. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. A native window no real pointer has entered gets no hover from Godot, so the runtime supplies it there and the answers say hover_supplied. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is mouse_drag. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held; mouse_drag (needs x, y, toX, toY, frames): a whole drag in one call, as a hand makes one: press at x, y, a motion each frame for frames frames, and release at toX, toY; for a stir, a paint stroke or a slow slider; joypad_button (needs button): press a controller button, or hold it, as an InputEventJoypadButton, so a game reading the pad in _input or _unhandled_input gets what a controller sends; an action bound to the button fires with it, as it would for a real one. Pressed and released a frame apart unless pressed says otherwise; joypad_motion (needs axis, value): move a stick or trigger axis to a value, as an InputEventJoypadMotion, which is what a game reading the sticks gets; the axis stays there until another motion moves it. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button, mouse motion or drag. All of it works headless, where the viewport is the project's window size and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. A native window no real pointer has entered gets no hover from Godot, so the runtime supplies it there and the answers say hover_supplied. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is mouse_drag. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held; mouse_drag (needs x, y, toX, toY, frames): a whole drag in one call, as a hand makes one: press at x, y, a motion each frame for frames frames, and release at toX, toY; for a stir, a paint stroke or a slow slider; joypad_button (needs button): press a controller button, or hold it, as an InputEventJoypadButton, so a game reading the pad in _input or _unhandled_input gets what a controller sends; an action bound to the button fires with it, as it would for a real one. Pressed and released a frame apart unless pressed says otherwise; joypad_motion (needs axis, value): move a stick or trigger axis to a value, as an InputEventJoypadMotion, which is what a game reading the sticks gets; the axis stays there until another motion moves it. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/runtime_inspect +++ observed/runtime_inspect { "annotations": { "openWorldHint": false, - "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, the performance metrics, or what it tells a screen reader. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. physics_steps_per_second is the physics steps taken a second over those frames, fewer than physics_ticks_per_second when a frame needs more than max_physics_steps_per_frame or the editor's Game view slows it. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count; accessibility: what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role (text for static text, button, check_box, group for a control holding others), name, value, description, placeholder, bounds in the window's pixels, the actions a reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one with the focus. node is the node that made the element; an element a node made as a part of itself has part_of, the node drawn around it that holds its words, or part_of_candidates where several are, and part: announcement marks the text a window announces through. An empty name is read as nothing: static text takes it from accessibility_update_set_value, and _note counts the text elements on screen with none. tooltip is the node's tooltip_text, which a Windows reader is not told. With nothing focused, the engine calls GdharnessRuntime focused. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or accessibility_support at Always Active, since the engine builds the tree only when asked; reading takes a second or two. Default tree.", + "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, the performance metrics, or what it tells a screen reader. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in reading order, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. physics_steps_per_second is the physics steps taken a second over those frames, fewer than physics_ticks_per_second when a frame needs more than max_physics_steps_per_frame or the editor's Game view slows it. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. Memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count; accessibility: what the game tells a screen reader, read through UI Automation as a Windows screen reader does, under windows: one tree per window, each element with its role (text for static text, button, check_box, group for a control holding others), name, value, value_text (a range's spoken words, such as \"75%\"), description, placeholder, bounds in the window's pixels, the actions a reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the focused one. node is the node that made the element; a part a node made of itself has part_of, the node drawn around it that holds its words, or part_of_candidates where several are, and part: announcement marks the text a window announces through. An empty name reads as nothing: static text takes it from accessibility_update_set_value, and _note counts the text elements on screen with none. tooltip is the node's tooltip_text, which a Windows reader is not told. With nothing focused, the engine calls GdharnessRuntime focused. Needs a windowed Windows run started with engineArgs [\"--accessibility\", \"always\"] or accessibility_support at Always Active, since the engine builds the tree only when asked; reading takes a second or two. Default tree.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "accessibility: only the elements whose name or value says these words, matched as find matches them, with what they hold. find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words. Only for ops find, accessibility.", + "description": "accessibility: only the elements whose name, value or value_text says these words, matched as find matches them, with what they hold. find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words. Only for ops find, accessibility.", "type": "string" }, "script": {
--- pinned/runtime_invoke +++ observed/runtime_invoke { "annotations": { "destructiveHint": true, - "description": "Sets a property or calls a method on a node, or on one of the engine singletons nodePath names, in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors and warnings the engine reported while a called method ran come back under errors, the first 20, with errors_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes. Operations: set (needs nodePath, property, value): set a property, on a node or on an object it holds; call (needs nodePath, method): call a method, on a node or on an object it holds, and return its result; result (needs requestId): collect the reply to a set or call whose wait ran out, by the requestId it answered with.", + "description": "Sets a property or calls a method on a node, or on one of the engine singletons nodePath names, in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors the engine reported while a called method ran come back under errors and warnings under warnings, the first 20 of each, with errors_not_shown and warnings_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes. Operations: set (needs nodePath, property, value): set a property, on a node or on an object it holds; call (needs nodePath, method): call a method, on a node or on an object it holds, and return its result; result (needs requestId): collect the reply to a set or call whose wait ran out, by the requestId it answered with.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "call: the arguments, fitted to the method's parameter types. A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused. Only for op call.", + "description": "call: the arguments, fitted to the method's parameter types. An RID is passed as the {\"_type\": \"RID\", \"id\": ...} a read answers it with, such as a viewport's for RenderingServer's render timing. A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {\"_type\": \"int\", \"value\": \"9007199254740993\"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused. A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused. Only for op call.", "type": "array" }, "method": { - "description": "set: the value, fitted to the property's type. A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}, which is the form a read answers with. \"\" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name. Only for op set." + "description": "set: the value, fitted to the property's type. A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {\"_type\": \"int\", \"value\": \"9007199254740993\"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused. A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}, which is the form a read answers with. \"\" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name. Only for op set." } }, "required": [
--- pinned/runtime_wait +++ observed/runtime_wait { "annotations": { "openWorldHint": false, - "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal (needs nodePath, signal): wait for a signal and answer with what it carried; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found; one already true when asked answers at once, with frames 0.", + "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal: wait for a signal and answer with what it carried; watch (needs nodePath, signal): start recording a signal and answer at once with a watch; arm it before the call meant to set the signal off, then collect it with signal and the watch. A client can run a call sent beside a wait only after the wait answers, so a wait sent with the call misses it; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found; one already true when asked answers at once, with frames 0.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "signal, until: the node. Needed for a signal and for a property; for says it defaults to /root, the whole screen. Only for ops signal, until.", + "description": "signal, watch, until: the node. Needed for a signal without a watch, a watch and a property; for says it defaults to /root, the whole screen. Only for ops signal, watch, until.", "type": "string" }, "op": { + "watch", "until" ], "type": "string" - "description": "signal: the signal name. Only for op signal.", + "description": "signal, watch: the signal name. Only for ops signal, watch.", "type": "string" }, "timeoutMs": { + }, + "watch": { + "description": "signal: the watch an earlier watch answered, in place of nodePath and signal. Answers at once, with fired_before_wait, when the signal fired after the watch was armed, and otherwise waits for it; times counts how often it fired, and args is what the first carried. The watch goes with the answer. Only for op signal.", + "minimum": 1, + "type": "integer" } }, "required": [
--- pinned/scene_animation +++ observed/scene_animation { "annotations": { "destructiveHint": false, - "description": "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. Needs the editor connected. Operations: read (needs playerNodePath): the animations of an AnimationPlayer, or one with its tracks; read_states (needs animTreePath): the states and transitions of an AnimationTree state machine; create (needs playerNodePath, animationName): a new animation in an AnimationPlayer; add_track (needs playerNodePath, animationName, track): a property or method track with keyframes; add_state (needs animTreePath, stateName, animationName): a state playing an animation, in an AnimationTree; connect_states (needs animTreePath, fromState, toState): a transition between two states.", + "description": "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. read and read_states need no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without running the scene's scripts. The ops that write need the editor connected. Operations: read (needs playerNodePath): the animations of an AnimationPlayer, or one with its tracks; read_states (needs animTreePath): the states and transitions of an AnimationTree state machine; create (needs playerNodePath, animationName): a new animation in an AnimationPlayer; add_track (needs playerNodePath, animationName, track): a property or method track with keyframes; add_state (needs animTreePath, stateName, animationName): a state playing an animation, in an AnimationTree; connect_states (needs animTreePath, fromState, toState): a transition between two states.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "add_state: the state to add. Only for op add_state.", + "description": "add_state: the state to add. It goes right of the rightmost state in the editor's graph, answered under position. Only for op add_state.", "type": "string" }, "step": {
--- pinned/scene_create +++ observed/scene_create { "annotations": { "destructiveHint": true, - "description": "Creates a scene file where none is, saves one, or saves a copy under a new path. A save of a scene open in the editor saves the editor's copy, unsaved changes included. Needs the editor connected. Operations: create: a new scene with one root node; save: save the scene as it is in the editor, or as the file loads when the editor does not have it open; from says which; save_as (needs newPath): save a copy under newPath. Default create.", + "description": "Creates a scene file where none is, saves one, or saves a copy under a new path. A save of a scene open in the editor saves the editor's copy, unsaved changes included. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: create: a new scene with one root node; save: save the scene as it is in the editor, or as the file loads when the editor does not have it open; from says which; save_as (needs newPath): save a copy under newPath. Default create.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/scene_node +++ observed/scene_node { "annotations": { "destructiveHint": true, - "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. set with name renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Needs the editor connected. Operations: add (needs nodeName): add a node of any class, or an instance of a scene; get (needs nodePath): read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent, or to another place among its siblings; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", + "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {\"name\": \"Boss\"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: add (needs nodeName): add a node of any class, or an instance of a scene; get (needs nodePath): read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent, or to another place among its siblings; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "Cells to place.", + "description": "Cells to place. Only for op set_tilemap_cells.", "items": { "additionalProperties": false, "properties": { - "description": "get: include properties still at their default. Default false.", + "description": "get: include properties still at their default. Default false. Only for op get.", "type": "boolean" }, "index": { - "description": "set_tilemap_cells: the layer of a TileMap. Default 0. A TileMapLayer is one layer.", + "description": "set_tilemap_cells: the layer of a TileMap. Default 0. A TileMapLayer is one layer. Only for op set_tilemap_cells.", "type": "number" }, "newName": { - "description": "duplicate: the copy's name.", + "description": "duplicate: the copy's name. Only for op duplicate.", "type": "string" }, "newParentPath": { - "description": "reparent: the new parent, which may be the one it has, to move it among its siblings.", + "description": "reparent: the new parent, which may be the one it has, to move it among its siblings. Only for op reparent.", "type": "string" }, "nodeName": { - "description": "add: the new node's name.", + "description": "add: the new node's name. Only for op add.", "type": "string" }, "nodePath": { - "description": "Node path from the scene root, such as \"Player/Sprite2D\". \".\" is the root.", + "description": "Node path from the scene root, such as \"Player/Sprite2D\". \".\" is the root. Only for ops get, set, duplicate, reparent, delete, set_tilemap_cells.", "type": "string" }, "nodeType": { - "description": "add: the node class, such as \"CharacterBody2D\". Give this or instancePath.", + "description": "add: the node class, such as \"CharacterBody2D\". Give this or instancePath. Only for op add.", "type": "string" }, "op": { - "description": "add: where the node goes. Default the root. duplicate: where the copy goes.", + "description": "add: where the node goes. Default the root. duplicate: where the copy goes. Only for ops add, duplicate.", "type": "string" }, "projectPath": { - "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: \"position:x\", \"modulate:a\", \"region_rect:size:y\"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds.", + "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: \"position:x\", \"modulate:a\", \"region_rect:size:y\"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds. A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {\"_type\": \"int\", \"value\": \"9007199254740993\"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused. Only for ops add, set.", "type": "object" }, "scenePath": {
--- pinned/scene_signal +++ observed/scene_signal { "annotations": { "destructiveHint": true, - "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. Needs the editor connected. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", + "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "connect: Object.ConnectFlags, such as 1 for deferred.", + "description": "connect: Object.ConnectFlags, such as 1 for deferred. Only for op connect.", "type": "number" }, "methodName": { - "description": "The method on that node, called when it emits.", + "description": "The method on that node, called when it emits. Only for ops connect, disconnect.", "type": "string" }, "nodePath": { - "description": "list: only connections involving this node.", + "description": "list: only connections involving this node. Only for op list.", "type": "string" }, "op": { - "description": "The signal it emits, such as \"pressed\".", + "description": "The signal it emits, such as \"pressed\". Only for ops connect, disconnect.", "type": "string" }, "sourceNodePath": { - "description": "The node that emits.", + "description": "The node that emits. Only for ops connect, disconnect.", "type": "string" }, "targetNodePath": { - "description": "The node whose method is called.", + "description": "The node whose method is called. Only for ops connect, disconnect.", "type": "string" } },
--- pinned/script_diagnostics +++ observed/script_diagnostics { "annotations": { "openWorldHint": false, - "description": "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", + "description": "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. With no editor of this project open, so no language server of it to ask, a short headless engine compiles the scripts instead and the answer says checkedBy: engine: every error that stops a script compiling, with its line and no column, dependenciesWithErrors naming the other scripts that failed while it loaded, and warnings null, since the engine reports warnings only to an editor. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/script_edit +++ observed/script_edit { "annotations": { "destructiveHint": true, - "description": "create: a class_name for the script. Not with content.", + "description": "create: a class_name for the script. Not with content. Only for op create.", "type": "string" }, "content": { - "description": "create: the whole file, written as given, instead of a template, className and extends.", + "description": "create: the whole file, written as given, instead of a template, className and extends. Only for op create.", "type": "string" }, "extends": { - "description": "create: the base class. Default Node. Not with content.", + "description": "create: the base class. Default Node. Not with content. Only for op create.", "type": "string" }, "modifications": { - "description": "Additions to make, in order. A name the script already declares at the top level, or one the call adds twice, refuses the call before anything is written.", + "description": "Additions to make, in order. A name the script already declares at the top level, or one the call adds twice, refuses the call before anything is written. Only for op modify.", "items": { "additionalProperties": false, "properties": { - "description": "Functions: the body.", + "description": "Functions: the body, its lines separated by \\n. Indent shared by every line is taken off and one tab put on, so it can be written indented or not.", "type": "string" }, "defaultValue": { - "description": "create: a starting shape.", + "description": "create: a starting shape. Only for op create.", "enum": [ "singleton", "state_machine",
--- pinned/script_info +++ observed/script_info { "annotations": { "openWorldHint": false, - "description": "What a script contains: its structure from the file, or symbols, completions and hover text from the editor's language server. Operations: structure: functions, variables, signals, class_name and extends, read from the file; symbols: document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration; completion (needs line, character): completions at a position, narrowed to the part of a name written before it, at most 100; hover (needs line, character): hover text at a position. Default structure.", + "description": "What a script contains: its structure from the file, or symbols, completions and hover text from the editor's language server. Operations: structure: functions, variables, signals, class_name and extends, read from the file, and dependencies: the paths it loads or preloads as written, then the scripts declaring each project class_name its code uses, which classes_used names with the class; autoloads_used names each autoload the code uses by its global name, with the autoload's script, which the project loads rather than this script, so it is not under dependencies; symbols: document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration; completion (needs line, character): completions at a position, narrowed to the part of a name written before it, at most 100; hover (needs line, character): hover text at a position. Default structure.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "completion, hover: the column, counted from 1 as the editor shows it, with a tab counted as one. symbols gives each name at the line and column hover is asked at.", + "description": "completion, hover: the column, counted from 1 as the editor shows it, with a tab counted as one. symbols gives each name at the line and column hover is asked at. Only for ops completion, hover.", "minimum": 1, "type": "integer" }, - "description": "structure: also list what the script inherits, walking extends through the project's other scripts. Each such member carries inherited_from, the file declaring it, and the answer gains inherits_from, the chain that was walked. A name the script overrides is listed at both its lines rather than once. A native base is not walked, since it declares nothing in a file: editor_classes info answers for those. Default false.", + "description": "structure: also list what the script inherits, walking extends through the project's other scripts. Each such member carries inherited_from, the file declaring it, and the answer gains inherits_from, the chain that was walked. A name the script overrides is listed at both its lines rather than once. A native base is not walked, since it declares nothing in a file: editor_classes info answers for those. Default false. Only for op structure.", "type": "boolean" }, "line": { - "description": "completion, hover: the line, counted from 1 as the editor shows it.", + "description": "completion, hover: the line, counted from 1 as the editor shows it. Only for ops completion, hover.", "minimum": 1, "type": "integer" },
Changed 2026-10-16 11:10 UTC
10 descriptions rewritten, 10 schema or annotation changes with the description left byte-identical.
--- pinned/debug_state +++ observed/debug_state { "annotations": { "openWorldHint": false, - "description": "variables: which frame, from a stack answer. Default the innermost. A frame the stack no longer holds is refused, and a scope whose values did not arrive in time answers variables null and is named under valuesMissing.", + "description": "variables: which frame, from a stack answer. Default the innermost. A frame the stack no longer holds is refused, and a scope whose values did not arrive in time answers variables null and is named under valuesMissing. Only for op variables.", "type": "number" }, "op": {
--- pinned/editor_classes +++ observed/editor_classes { "annotations": { "openWorldHint": false, - "description": "info: one member by name, a method, property, signal, enum or constant, with its signature and which class declares it, looked for on the class and every ancestor. The whole of Control is seventy thousand characters; the question \"does this engine have it, and what does it take\" is this. A name nothing declares is refused with the members whose names contain it.", + "description": "info: one member by name, a method, property, signal, enum or constant, with its signature and which class declares it, looked for on the class and every ancestor. The whole of Control is seventy thousand characters; the question \"does this engine have it, and what does it take\" is this. A name nothing declares is refused with the members whose names contain it. Only for op info.", "type": "string" }, "op": {
--- pinned/editor_launch +++ observed/editor_launch { "annotations": { "destructiveHint": true, - "description": "Open the editor headless and, on Windows, on a desktop of its own, so no window of it or of the games it plays appears. Default false: one visible editor, shown without taking the focus. On restart only for an editor a server opened; one opened by hand comes back as it was, so hidden is refused there.", + "description": "Open the editor headless and, on Windows, on a desktop of its own, so no window of it or of the games it plays appears. Default false: one visible editor, shown without taking the focus. On restart only for an editor a server opened; one opened by hand comes back as it was, so hidden is refused there. Only for ops open, restart.", "type": "boolean" }, "op": { - "description": "Absolute path to the project directory, the one holding project.godot.", + "description": "Absolute path to the project directory, the one holding project.godot. Only for op open.", "type": "string" } },
--- pinned/editor_output +++ observed/editor_output { "annotations": { "openWorldHint": false, - "description": "How many lines above a repeated group to carry with it, counting only lines that are not themselves part of a group. Default 3. The line that explains a wall of identical errors is the last ordinary line above it, so this walks past the wall rather than a fixed distance back.", + "description": "How many lines above a repeated group to carry with it, counting only lines that are not themselves part of a group. Default 3. The line that explains a wall of identical errors is the last ordinary line above it, so this walks past the wall rather than a fixed distance back. Only for op editor.", "minimum": 0, "type": "integer" }, - "description": "Also answer with cpuSeconds, the processor time the run has used. Default false, because asking costs a subprocess. Worth it when elapsedMs is climbing and nothing is being printed: processor time standing still is a run that has stopped doing anything, rather than one that is slow.", + "description": "Also answer with cpuSeconds, the processor time the run has used. Default false, because asking costs a subprocess. Worth it when elapsedMs is climbing and nothing is being printed: processor time standing still is a run that has stopped doing anything, rather than one that is slow. Only for op run.", "type": "boolean" }, "limit": { - "description": "Only entries this has not already answered with at this severity. Default false. Measured per severity floor and not across them, so polling for errors leaves the rest of the output for a later read rather than consuming it, and a read with contains is a search over the whole run and consumes nothing.", + "description": "Only entries this has not already answered with at this severity. Default false. Measured per severity floor and not across them, so polling for errors leaves the rest of the output for a later read rather than consuming it, and a read with contains is a search over the whole run and consumes nothing. Only for op run.", "type": "boolean" } },
--- pinned/editor_rescan +++ observed/editor_rescan { "annotations": { "destructiveHint": false, - "description": "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. What the reload changed comes back under methodsAdded, methodsRemoved, constantsAdded and constantsRemoved, with each enum value as Kind.SHORT among the constants, since an enum member is held as a constant and never shows among the methods; methodsHeld and constantsHeld count what the copy holds afterwards, because a reload that compiled nothing and answered OK is the failure worth catching. listMembers adds the whole lists. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before. A @tool script is not reloaded either, since it runs inside the editor and reloading one from inside a call can take the editor down: reloadProblem says so, and editor_launch restart recompiles it. A path spelled in another case than the file is reloaded under the spelling on disk, which is the one the editor holds its copy under, and that spelling comes back under reloadedAs.", + "description": "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. What the reload changed comes back under methodsAdded, methodsRemoved, constantsAdded and constantsRemoved, with each enum value as Kind.SHORT among the constants, since an enum member is held as a constant and never shows among the methods; methodsHeld and constantsHeld count what the copy holds afterwards, because a reload that compiled nothing and answered OK is the failure worth catching. listMembers adds the whole lists. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before. A @tool script is not reloaded either, since it runs inside the editor and reloading one from inside a call can take the editor down: reloadProblem says so, and editor_launch restart recompiles it. A path spelled in another case than the file is reloaded under the spelling on disk, which is the one the editor holds its copy under, and that spelling comes back under reloadedAs. A script open in a script tab with unsaved edits is reloaded from the file all the same, and unsavedInEditorNote says that saving the tab writes its text back over the file; open tabs with no unsaved edits reread what changed, as they do when the editor's window takes the focus.", "type": "string" }, "timeoutMs": {
--- pinned/editor_run +++ observed/editor_run { "annotations": { "destructiveHint": true, - "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run, naming the process ended under endedPid, and answer with the errors and warnings it printed under entries (editor_output has the rest); a game that started processes of its own keeps them unless andChildren says to end those too; check (needs projectPath): boot headless, quit after a few frames, and report the verdict: booted is true only for a run that quit on its own within timeoutMs, exited 0 and printed no error, so a game that reached _ready and then raised an error answers false; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended, sorted by self time. A run started with profile: true is profiled from its start; a game the editor plays that was not is profiled from the moment this is asked; a run this server started without it is refused, since its debugger is fixed as it starts. Default start. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", + "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run, asking the game to close first unless graceful is false, naming the process ended under endedPid, and answer with the errors and warnings it printed under entries (editor_output has the rest); a game that started processes of its own keeps them unless andChildren says to end those too; check (needs projectPath): boot headless, quit after a few frames, and report the verdict: booted is true only for a run that quit on its own within timeoutMs, exited 0 and printed no error, so a game that reached _ready and then raised an error answers false; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended, sorted by self time. A run started with profile: true is profiled from its start; a game the editor plays that was not is profiled from the moment this is asked; a run this server started without it is refused, since its debugger is fixed as it starts. Default start. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "stop: also end what the game started for itself, with OS.create_process or otherwise, such as a bench's workers and whatever those started in turn. Only the processes under the run's, to any depth, that are games of this project are ended: announced as one, or this project's engine run with --path on this project by its command line, which is how a bench whose workers deliberately carry no runtime is reached. They are named under endedChildren, with what identified each under identifiedBy; any other process under the run is left and named under childrenLeft, and childrenUnknown is true when nothing could be listed, with the note saying whether the platform would not or the run has no process to list under. Default false, and the run's process is ended either way.", + "description": "stop: also end what the game started for itself, with OS.create_process or otherwise, such as a bench's workers and whatever those started in turn. Only the processes under the run's, to any depth, that are games of this project are ended: announced as one, or this project's engine run with --path on this project by its command line, which is how a bench whose workers deliberately carry no runtime is reached. They are named under endedChildren, with what identified each under identifiedBy; any other process under the run is left and named under childrenLeft, and childrenUnknown is true when nothing could be listed, with the note saying whether the platform would not or the run has no process to list under. Default false, and the run's process is ended either way. Only for op stop.", "type": "boolean" }, "args": { - "description": "The game's own arguments, what OS.get_cmdline_user_args() answers, such as [\"--level=2\"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so its debugger is the keeper holding it rather than the editor; the debug_* tools answer for it all the same.", + "description": "The game's own arguments, what OS.get_cmdline_user_args() answers, such as [\"--level=2\"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so its debugger is the keeper holding it rather than the editor; the debug_* tools answer for it all the same. Only for ops start, check.", "items": { "type": "string" }, - "description": "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed. A property's get or set block, which the profile names Class.@name_getter or Class.@name_setter, is asked about by that name or by the property's: the places are where the property is named, each saying instead of called whether it writes the property (access write), which runs the set block, or reads it (access read), which runs the get block, and the rows are both blocks'. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not places.", + "description": "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed. A property's get or set block, which the profile names Class.@name_getter or Class.@name_setter, is asked about by that name or by the property's: the places are where the property is named, each saying instead of called whether it writes the property (access write), which runs the set block, or reads it (access read), which runs the get block, and the rows are both blocks'. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not places. Only for op profile.", "type": "string" }, "engineArgs": { - "description": "The engine's own flags, before the separator, such as [\"--verbose\"] or [\"--fixed-fps\", \"60\"], a value going as the next entry. Only those that change what the engine logs, draws for debugging, checks on the GPU, which renderer draws, how it times frames or whether it builds the tree a screen reader reads, and Movie Maker's --write-movie: --verbose, --quiet, --debug-collisions, --debug-paths, --debug-navigation, --debug-avoidance, --debug-stringnames, --debug-canvas-item-redraw, --debug-mute-audio, --print-fps, --gpu-profile, --gpu-validation, --gpu-abort, --disable-vsync, --frame-delay, --time-scale, --fixed-fps, --max-fps, --resolution, --write-movie, --rendering-driver, --rendering-method, --accessibility. Any other is refused, since the engine's command line can also run scripts, open other projects and write files. --rendering-driver takes vulkan, d3d12, metal, opengl3, opengl3_es, opengl3_angle or dummy, and --rendering-method forward_plus, mobile or gl_compatibility, so the same scene can be measured on another driver and a project's OpenGL fallback exercised on a machine that has Vulkan; they choose what draws a run with a window. --accessibility always has the game build the tree a screen reader reads, as the project's accessibility/general/accessibility_support at Always Active does, so runtime_inspect accessibility can read it; otherwise the engine builds it only when a screen reader is running. --write-movie takes an absolute path outside the project, in a directory that exists, ending in .avi (one file, video and sound) or .png (one numbered PNG per frame and a .wav beside them), where nothing it would write is there already; it needs a start with headless: false, since the engine crashes writing a movie headless, and it records at the project's editor/movie_writer/fps unless --fixed-fps says otherwise. A run with any is started by this server, as one with args is. A check given --verbose answers with everything printed rather than only errors and warnings, since what verbose adds, the instances leaked at exit among it, is printed as ordinary lines.", + "description": "The engine's own flags, before the separator, such as [\"--verbose\"] or [\"--fixed-fps\", \"60\"], a value going as the next entry. Only those that change what the engine logs, draws for debugging, checks on the GPU, which renderer draws, how it times frames or whether it builds the tree a screen reader reads, and Movie Maker's --write-movie: --verbose, --quiet, --debug-collisions, --debug-paths, --debug-navigation, --debug-avoidance, --debug-stringnames, --debug-canvas-item-redraw, --debug-mute-audio, --print-fps, --gpu-profile, --gpu-validation, --gpu-abort, --disable-vsync, --frame-delay, --time-scale, --fixed-fps, --max-fps, --resolution, --write-movie, --rendering-driver, --rendering-method, --accessibility. Any other is refused, since the engine's command line can also run scripts, open other projects and write files. --rendering-driver takes vulkan, d3d12, metal, opengl3, opengl3_es, opengl3_angle or dummy, and --rendering-method forward_plus, mobile or gl_compatibility, so the same scene can be measured on another driver and a project's OpenGL fallback exercised on a machine that has Vulkan; they choose what draws a run with a window. --accessibility always has the game build the tree a screen reader reads, as the project's accessibility/general/accessibility_support at Always Active does, so runtime_inspect accessibility can read it; otherwise the engine builds it only when a screen reader is running. --write-movie takes an absolute path outside the project, in a directory that exists, ending in .avi (one file, video and sound) or .png (one numbered PNG per frame and a .wav beside them), where nothing it would write is there already; it needs a start with headless: false, since the engine crashes writing a movie headless, and it records at the project's editor/movie_writer/fps unless --fixed-fps says otherwise. A run with any is started by this server, as one with args is. A check given --verbose answers with everything printed rather than only errors and warnings, since what verbose adds, the instances leaked at exit among it, is printed as ordinary lines. Only for ops start, check.", "items": { "type": "string" }, - "description": "Environment variables for this run alone, as a map of strings, over the server's own environment. Like savesIn, a run carrying any is started by this server rather than played by the editor. Names beginning with GDHARNESS_ are refused: they are this server's contract with the addon in the game, and the runtime directory is set here whatever the rest of the environment says, so a game that moves TEMP still announces where this server looks.", + "description": "Environment variables for this run alone, as a map of strings, over the server's own environment. Like savesIn, a run carrying any is started by this server rather than played by the editor. Names beginning with GDHARNESS_ are refused: they are this server's contract with the addon in the game, and the runtime directory is set here whatever the rest of the environment says, so a game that moves TEMP still announces where this server looks. Only for ops start, check.", "type": "object" }, "executable": { - "description": "start: an exported build to run instead of the project, as a path inside the project such as export/windows/game.exe, so what a player runs can be measured. It takes args, engineArgs, savesIn, env, headless and visible as the project does, goes on the hidden desktop the same way, and is ended by stop, but takes no scene (its pack holds the main scene) and no profile, and has no debugger: a release build quits when given one. Its runtime is not waited for.", + "description": "start: an exported build to run instead of the project, as a path inside the project such as export/windows/game.exe, so what a player runs can be measured. It takes args, engineArgs, savesIn, env, headless and visible as the project does, goes on the hidden desktop the same way, and is ended by stop, but takes no scene (its pack holds the main scene) and no profile, and has no debugger: a release build quits when given one. Its runtime is not waited for. Only for op start.", "type": "string" }, "frames": { - "description": "check: frames to run before quitting. Default 3.", + "description": "check: frames to run before quitting. Default 3. Only for op check.", "minimum": 1, "type": "integer" }, + "graceful": { + "description": "stop, and a start that ends the run going before it: ask the game to close first, the way a player's close asks it, so its own handling of a close runs, which a game keeping a crash mark needs to take the mark down. The runtime addon delivers the close request to every node and then the root window's close_requested, and a tree with auto_accept_quit on then quits. The game is given ten seconds to exit, GDHARNESS_CLOSE_WAIT_MS sets another, and is ended the ordinary way when it does not, or when it has no runtime listening, an older addon, or does not answer within three seconds, as a game held at a breakpoint does not; notClosedBecause (previousRunNotClosedBecause on a start) then says which. Default true; false ends the process at once. Only for ops start, stop.", + "type": "boolean" + }, "headless": { - "description": "start: force a window or no window.", + "description": "start: force a window or no window. Only for op start.", "type": "boolean" }, "limit": { - "description": "profile: how many functions to answer with, those with the most self time. Default 30.", + "description": "profile: how many functions to answer with, those with the most self time. Default 30. Only for op profile.", "minimum": 1, "type": "integer" }, - "description": "stop: a game of this project that no server here is holding, by its process id as editor_status lists it under runtimes: one a server before a reconnect started and left no note for, or one started some other way. It is ended only when it announced itself for this project and the operating system describes it as this project's engine run, --path on the project and no editor flag, started before it announced; otherwise nothing is signalled and the answer says which of those failed. The run this server holds is ended the ordinary way whether or not its number is given.", + "description": "stop: a game of this project that no server here is holding, by its process id as editor_status lists it under runtimes: one a server before a reconnect started and left no note for, or one started some other way. It is ended only when it announced itself for this project and the operating system describes it as this project's engine run, --path on the project and no editor flag, started before it announced; otherwise nothing is signalled and the answer says which of those failed. The run this server holds is ended the ordinary way whether or not its number is given. Only for op stop.", "minimum": 1, "type": "integer" }, - "description": "start: profile the run's GDScript with the engine's own script profiler from its start, for editor_run profile to answer which functions took the time. A run this server starts dials the process that keeps it as its debugger, on loopback, with breakpoints and error breaks skipped so it never stops to wait; a game the editor plays is profiled through the editor's debugger session. Needs an engine built with debugging, which the editor binary is and a release export template is not. The start answers profile.on and, under profile.totals, how the numbers will arrive. Default false.", + "description": "start: profile the run's GDScript with the engine's own script profiler from its start, for editor_run profile to answer which functions took the time. A run this server starts dials the process that keeps it as its debugger, on loopback, with breakpoints and error breaks skipped so it never stops to wait; a game the editor plays is profiled through the editor's debugger session. Needs an engine built with debugging, which the editor binary is and a release export template is not. The start answers profile.on and, under profile.totals, how the numbers will arrive. Default false. Only for op start.", "type": "boolean" }, "projectPath": { - "description": "start: how long to wait for the game to announce its runtime before answering. Default 5000, or half as long again as the slowest of the last five boots of the same scene when that is more, up to 60000: the server notes how long each game took to announce, per scene and for the main scene when none is named, since a boot moves with the load on the machine, so a scene that boots slowly is waited for from its second start on without being asked to, and a harness scene that announces late sets nothing for the main scene. What it waits for is the first frame, so everything the game does before drawing one is inside it, including work the args just asked for: a flag that simulates six years of game time before anything is drawn makes the announcement that late, on a project that announces promptly without it. Raise it for such a run rather than reading runtime listening false as a fault. 0 does not wait at all, which is the one to pass for a scene that announces nothing by construction, such as a bench that prints and quits.", + "description": "start: how long to wait for the game to announce its runtime before answering. Default 5000, or half as long again as the slowest of the last five boots of the same scene when that is more, up to 60000: the server notes how long each game took to announce, per scene and for the main scene when none is named, since a boot moves with the load on the machine, so a scene that boots slowly is waited for from its second start on without being asked to, and a harness scene that announces late sets nothing for the main scene. What it waits for is the first frame, so everything the game does before drawing one is inside it, including work the args just asked for: a flag that simulates six years of game time before anything is drawn makes the announcement that late, on a project that announces promptly without it. Raise it for such a run rather than reading runtime listening false as a fault. 0 does not wait at all, which is the one to pass for a scene that announces nothing by construction, such as a bench that prints and quits. Only for op start.", "minimum": 0, "type": "integer" }, - "description": "An absolute directory for this run's user://, so a game that saves writes nowhere near the copy somebody plays: opening a save writes the one being left, and a game that autosaves writes on its own. Godot takes no flag for it, so this is the environment, and the run is started by this server rather than played by the editor, which cannot be given one. It does not have to exist: the engine makes it, and the Godot/app_userdata/<project> tree under it, measured on 4.7.2. On macOS the engine reads user:// off HOME and ignores it, also measured, and the answer says so under savesNote rather than leaving the caller to find out from the player's save list.", + "description": "An absolute directory for this run's user://, so a game that saves writes nowhere near the copy somebody plays: opening a save writes the one being left, and a game that autosaves writes on its own. Godot takes no flag for it, so this is the environment, and the run is started by this server rather than played by the editor, which cannot be given one. It does not have to exist: the engine makes it, and the Godot/app_userdata/<project> tree under it, measured on 4.7.2. On macOS the engine reads user:// off HOME and ignores it, also measured, and the answer says so under savesNote rather than leaving the caller to find out from the player's save list. Only for ops start, check.", "type": "string" }, "scene": { - "description": "A scene to run instead of the main scene.", + "description": "A scene to run instead of the main scene. Only for ops start, check.", "type": "string" }, "silent": { - "description": "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, whose keeper is its debugger rather than the editor. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before.", + "description": "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, whose keeper is its debugger rather than the editor. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before. Only for op start.", "type": "boolean" }, "timeoutMs": { - "description": "check: how long to give the boot before it is called hung. Default 60000. wait: how long to wait for the run to end before answering anyway. Default 600000.", + "description": "check: how long to give the boot before it is called hung. Default 60000. wait: how long to wait for the run to end before answering anyway. Default 600000. Only for ops check, wait.", "minimum": 1, "type": "integer" }, - "description": "start: on Windows, show the window on the desktop in use. By default a windowed run this server starts goes on a desktop of its own, where it renders and runtime_capture works but the window never shows and never takes the keyboard. A game a visible editor plays opens on the desktop in use, and one a hidden editor (editor_launch hidden: true) plays goes on its desktop. Elsewhere than Windows the window shows either way. Default false.", + "description": "start: on Windows, show the window on the desktop in use. By default a windowed run this server starts goes on a desktop of its own, where it renders and runtime_capture works but the window never shows and never takes the keyboard. A game a visible editor plays opens on the desktop in use, and one a hidden editor (editor_launch hidden: true) plays goes on its desktop. Elsewhere than Windows the window shows either way. Default false. Only for op start.", "type": "boolean" } },
--- pinned/editor_status +++ observed/editor_status { "annotations": { "openWorldHint": false, - "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and which games with the runtime addon are reachable, of this project and of every other on the machine. Read connected with mayYetConnect: the editor dials this server and backs off between tries, so for the first half-minute after a server starts false means not reached yet as often as no editor; mayYetConnect true is worth waiting out, false is final, and restartInterrupted, restartUnderway, closedHere and editorsNotYetConnected say why. An editor that has connected and not yet greeted reports nothing about itself, and every other call made in that window waits up to ten seconds for it to settle. playingInEditor null means unanswered rather than nothing playing: processActive and runtimes do not depend on the editor answering. game.run names the run editor_output and editor_run stop answer for, and stays named after it ends. addonIsStale, on the editor and on each game, says it runs addon code other than what this server ships, and staleNote says which half moves: gdharness upgrade in the project, a restart, or a reconnect of this server. A false editor.lspListening or editor.dapListening comes with serversNote, and editor_launch restart binds the port again. editor.scan says whether the editor is scanning, which editor_run waits out before starting a game. A game announced in a protocol this server does not speak is listed under unreadable, during an upgrade that moves the protocol, until the halves match. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", + "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and which games with the runtime addon are reachable, of this project and of every other on the machine. Read connected with mayYetConnect: the editor dials this server and backs off between tries, so for the first half-minute after a server starts false means not reached yet as often as no editor; mayYetConnect true is worth waiting out, false is final, and editorCannotConnect, restartInterrupted, restartUnderway, closedHere and editorsNotYetConnected say why. An editor that has connected and not yet greeted reports nothing about itself, and every other call made in that window waits up to ten seconds for it to settle. playingInEditor null means unanswered rather than nothing playing: processActive and runtimes do not depend on the editor answering. game.run names the run editor_output and editor_run stop answer for, and stays named after it ends. addonIsStale, on the editor and on each game, says it runs addon code other than what this server ships, and staleNote says which half moves: the project, by a gdharness upgrade it spells out to run in a shell, the editor by a restart, or this server by a reconnect. A false editor.lspListening or editor.dapListening comes with serversNote, and editor_launch restart binds the port again. editor.scan says whether the editor is scanning, which editor_run waits out before starting a game. A game announced in a protocol this server does not speak is listed under unreadable, during an upgrade that moves the protocol, until the halves match. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": {},
--- pinned/project_settings +++ observed/project_settings { "annotations": { "destructiveHint": true, - "description": "Input action name, such as \"jump\".", + "description": "Input action name, such as \"jump\". Only for ops add_input_action, remove_input_action.", "type": "string" }, "busIndex": { - "description": "Audio bus index.", + "description": "Audio bus index. Only for ops set_audio_bus_effect, set_audio_bus_volume.", "type": "number" }, "busName": { - "description": "Audio bus name.", + "description": "Audio bus name. Only for ops add_audio_bus, remove_audio_bus.", "type": "string" }, "deadzone": { - "description": "Input actions: analogue deadzone, 0 to 1. Default 0.5.", + "description": "Input actions: analogue deadzone, 0 to 1. Default 0.5. Only for op add_input_action.", "maximum": 1, "minimum": 0, "type": "number" - "description": "Slot on the bus for the effect: the effect there is replaced, and one past the last adds one. Any other slot is refused.", + "description": "Slot on the bus for the effect: the effect there is replaced, and one past the last adds one. Any other slot is refused. Only for op set_audio_bus_effect.", "type": "number" }, "effectType": { - "description": "Effect class, such as \"AudioEffectReverb\".", + "description": "Effect class, such as \"AudioEffectReverb\". Only for op set_audio_bus_effect.", "type": "string" }, "enabled": { - "description": "add_autoload: Godot 4 loads every autoload in the list, so false is refused. Use global: false to keep the name out of the global scope, or remove_autoload to stop it loading. set_audio_bus_effect: whether the effect is on. Default true.", + "description": "add_autoload: Godot 4 loads every autoload in the list, so false is refused. Use global: false to keep the name out of the global scope, or remove_autoload to stop it loading. set_audio_bus_effect: whether the effect is on. Default true. Only for ops add_autoload, set_audio_bus_effect.", "type": "boolean" }, "events": { - "description": "The events that trigger the action.", + "description": "The events that trigger the action. Only for op add_input_action.", "items": { "additionalProperties": false, "properties": { - "description": "get: where to read. Default disk, which starts a short-lived engine and reads project.godot, needs no editor and is what an automatic check can reproduce. editor asks the open editor instead, which costs no engine and answers what the editor holds, including changes nobody has saved. Asking for editor with none connected is refused rather than answered from disk, so an answer never means the other one quietly.", + "description": "get: where to read. Default disk, which starts a short-lived engine and reads project.godot, needs no editor and is what an automatic check can reproduce. editor asks the open editor instead, which costs no engine and answers what the editor holds, including changes nobody has saved. Asking for editor with none connected is refused rather than answered from disk, so an answer never means the other one quietly. Only for op get.", "enum": [ "disk", "editor" - "description": "Autoloads: make the name a global variable, the * in project.godot and the editor's \"Global Variable\" column. Default true. The autoload loads either way.", + "description": "Autoloads: make the name a global variable, the * in project.godot and the editor's \"Global Variable\" column. Default true. The autoload loads either way. Only for op add_autoload.", "type": "boolean" }, "name": { - "description": "Autoload name.", + "description": "Autoload name. Only for ops add_autoload, remove_autoload.", "type": "string" }, "op": { - "description": "Audio buses: the bus to send to. Default 0, Master. The new bus goes right after it, so every later bus moves one along, and the answer lists them all.", + "description": "Audio buses: the bus to send to. Default 0, Master. The new bus goes right after it, so every later bus moves one along, and the answer lists them all. Only for op add_audio_bus.", "type": "number" }, "path": { - "description": "Autoload script or scene inside the project.", + "description": "Autoload script or scene inside the project. Only for op add_autoload.", "type": "string" }, "pluginName": { - "description": "Folder name under addons/.", + "description": "Folder name under addons/. Only for ops enable_plugin, disable_plugin.", "type": "string" }, "prefix": { - "description": "get: answer with every setting whose name starts with this, and the type the engine registers for each, instead of one setting by name. \"debug/gdscript/warnings/\" answers the whole family. The type matters: a family of levels can hold a setting that is a bool, and writing a level over it looks like it worked.", + "description": "get: answer with every setting whose name starts with this, and the type the engine registers for each, instead of one setting by name. \"debug/gdscript/warnings/\" answers the whole family. The type matters: a family of levels can hold a setting that is a bool, and writing a level over it looks like it worked. Only for op get.", "type": "string" }, "projectPath": { - "description": "Scene file inside the project, such as \"scenes/main.tscn\" or \"res://scenes/main.tscn\".", + "description": "Scene file inside the project, such as \"scenes/main.tscn\" or \"res://scenes/main.tscn\". Only for op set_main_scene.", "type": "string" }, "setting": { - "description": "Setting path, such as \"display/window/size/viewport_width\".", + "description": "Setting path, such as \"display/window/size/viewport_width\". Only for ops get, set.", "type": "string" }, "value": { - "description": "The value to write. Engine types may be tagged, {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, and infinity as {\"_type\": \"float\", \"value\": \"inf\"}, the form a read answers with. \"\" writes an empty string." + "description": "The value to write. Engine types may be tagged, {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, and infinity as {\"_type\": \"float\", \"value\": \"inf\"}, the form a read answers with. \"\" writes an empty string. Only for op set." }, "volumeDb": { - "description": "Bus volume in decibels.", + "description": "Bus volume in decibels. Only for op set_audio_bus_volume.", "type": "number" } },
--- pinned/resource_edit +++ observed/resource_edit { "annotations": { "destructiveHint": true, - "description": "Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as \"Button/colors/font_color\". Needs the editor connected. Operations: read: the properties a resource file sets; create (needs resourceType): a new resource of any class; modify (needs properties): set properties on an existing resource; create_shader (needs shaderType): a .gdshader file; create_tileset (needs sources): a TileSet from texture atlases; set_theme_color (needs controlType, colorName, color): a colour in a Theme; set_theme_font_size (needs controlType, fontSizeName, size): a font size in a Theme.", + "description": "Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as \"Button/colors/font_color\". move takes any file of the project, a script or an image as much as a resource, or a directory, and works from the files, so it needs no editor; one serving the project is brought up to it as after a move in its own FileSystem dock, its open scenes and cached resources named for the new paths and the project settings the move rewrote taken from the file. read needs no editor either: with none serving the project, a headless engine reads the file. The ops that write a resource need the editor connected. Operations: read: the properties a resource file sets; create (needs resourceType): a new resource of any class; modify (needs properties): set properties on an existing resource; create_shader (needs shaderType): a .gdshader file; create_tileset (needs sources): a TileSet from texture atlases; set_theme_color (needs controlType, colorName, color): a colour in a Theme; set_theme_font_size (needs controlType, fontSizeName, size): a font size in a Theme; move (needs newPath): move or rename a file or a directory, and every path in the project naming it.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "create_shader: the shader source. Default: a minimal shader of that type.", + "description": "create_shader: the shader source. Default: a minimal shader of that type. Only for op create_shader.", "type": "string" }, "color": { - "description": "set_theme_color: r, g and b from 0 to 1, with a optional and opaque by default.", + "description": "set_theme_color: r, g and b from 0 to 1, with a optional and opaque by default. Only for op set_theme_color.", "properties": { "a": { "type": "number" - "description": "set_theme_color: such as \"font_color\".", + "description": "set_theme_color: such as \"font_color\". Only for op set_theme_color.", "type": "string" }, "controlType": { - "description": "Theme ops: the Control class, such as \"Button\".", + "description": "Theme ops: the Control class, such as \"Button\". Only for ops set_theme_color, set_theme_font_size.", "type": "string" }, "fontSizeName": { - "description": "set_theme_font_size: such as \"font_size\".", + "description": "set_theme_font_size: such as \"font_size\". Only for op set_theme_font_size.", "type": "string" }, "includeDefaults": { - "description": "read: include properties still at their default. Default false.", + "description": "read: include properties still at their default. Default false. Only for op read.", "type": "boolean" }, + "newPath": { + "description": "move: where it goes, inside the project, and for a file with the extension it has. A file goes with its .import and .uid, and a directory with everything in it. Every path naming what moved in a scene, resource, script, project.godot or other text file is rewritten in the style it was written in, res:// or relative, and so is every relative path inside a moved file. A string naming it any other way, such as the start of a path built at run time, is answered under leftAlone rather than changed, and so is a scene or resource saved as .scn or .res, which this cannot rewrite. Only for op move.", + "type": "string" + }, "op": { "description": "What to do.", "enum": [ - "set_theme_font_size" + "set_theme_font_size", + "move" ], "type": "string" }, + "preview": { + "description": "move: answer with what would move and change, and write nothing. Default false. Only for op move.", + "type": "boolean" + }, "projectPath": { "description": "Absolute path to the project directory, the one holding project.godot.", "type": "string" - "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds.", + "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: \"position:x\", \"modulate:a\", \"region_rect:size:y\"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds. Only for ops create, modify.", "type": "object" }, "resourcePath": { - "description": "File inside the project, such as \"sprites/hero.png\" or \"materials/steel.tres\".", + "description": "File inside the project, such as \"sprites/hero.png\" or \"materials/steel.tres\". move: a directory too.", "type": "string" }, "resourceType": { - "description": "create: the resource class, such as \"PhysicsMaterial\" or \"StandardMaterial3D\".", + "description": "create: the resource class, such as \"PhysicsMaterial\" or \"StandardMaterial3D\". Only for op create.", "type": "string" }, "script": { - "description": "create: a script to attach, for custom resources.", + "description": "create: a script to attach, for custom resources. Only for op create.", "type": "string" }, "shaderType": { - "description": "create_shader.", + "description": "create_shader. Only for op create_shader.", "enum": [ "canvas_item", "spatial", - "description": "set_theme_font_size: pixels.", + "description": "set_theme_font_size: pixels. Only for op set_theme_font_size.", "type": "number" }, "sources": { - "description": "Atlas sources, one per texture.", + "description": "Atlas sources, one per texture. Only for op create_tileset.", "items": { "additionalProperties": false, "properties": {
--- pinned/runtime_capture +++ observed/runtime_capture { "annotations": { "openWorldHint": false, - "description": "screenshot: capture only where this node is drawn, as runtime_inspect rect answers it: a Control's rectangle, what a 3D node's geometry covers, or what a Sprite2D draws. A node with a position and no extent is refused; give region around it instead. Not with region.", + "description": "screenshot: capture only where this node is drawn, as runtime_inspect rect answers it: a Control's rectangle, what a 3D node's geometry covers, or what a Sprite2D draws. A node with a position and no extent is refused; give region around it instead. Not with region. Only for op screenshot.", "type": "string" }, "op": { - "description": "screenshot: capture only this rectangle of the window, at the game's own pixels: x, y, width and height in window pixels, or the window rectangle runtime_inspect rect answers, passed as it came. A region partly outside the window is cut to it and the answer says clipped; one wholly outside is refused. The answer names the region captured in window pixels, and drawnAt when the project draws at its own size and stretches that to the window, since the picture then has the game's pixels rather than the window's.", + "description": "screenshot: capture only this rectangle of the window, at the game's own pixels: x, y, width and height in window pixels, or the window rectangle runtime_inspect rect answers, passed as it came. A region partly outside the window is cut to it and the answer says clipped; one wholly outside is refused. The answer names the region captured in window pixels, and drawnAt when the project draws at its own size and stretches that to the window, since the picture then has the game's pixels rather than the window's. Only for op screenshot.", "type": "object" }, "viewportPath": { - "description": "viewport: the Viewport node. Default the root viewport.", + "description": "viewport: the Viewport node. Default the root viewport. Only for op viewport.", "type": "string" }, "width": {
--- pinned/runtime_input +++ observed/runtime_input { "annotations": { "destructiveHint": false, - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release; joypad_button (needs button): press a controller button, or hold it, as an InputEventJoypadButton, so a game reading the pad in _input or _unhandled_input gets what a controller sends; an action bound to the button fires with it, as it would for a real one. Pressed and released a frame apart unless pressed says otherwise; joypad_motion (needs axis, value): move a stick or trigger axis to a value, as an InputEventJoypadMotion, which is what a game reading the sticks gets; the axis stays there until another motion moves it. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button, mouse motion or drag. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. A native window no real pointer has entered gets no hover from Godot, so the runtime supplies it there and the answers say hover_supplied. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is mouse_drag. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held; mouse_drag (needs x, y, toX, toY, frames): a whole drag in one call, as a hand makes one: press at x, y, a motion each frame for frames frames, and release at toX, toY; for a stir, a paint stroke or a slow slider; joypad_button (needs button): press a controller button, or hold it, as an InputEventJoypadButton, so a game reading the pad in _input or _unhandled_input gets what a controller sends; an action bound to the button fires with it, as it would for a real one. Pressed and released a frame apart unless pressed says otherwise; joypad_motion (needs axis, value): move a stick or trigger axis to a value, as an InputEventJoypadMotion, which is what a game reading the sticks gets; the axis stays there until another motion moves it. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "action: the InputMap action name. An engine dialog is not answered this way: AcceptDialog reads the Escape key itself and never asks the InputMap, so ui_cancel goes in and the question stays up. Dismiss one with the key op and keycode Escape, or click its button.", + "description": "action: the InputMap action name. An engine dialog is not answered this way: AcceptDialog reads the Escape key itself and never asks the InputMap, so ui_cancel goes in and the question stays up. Dismiss one with the key op and keycode Escape, or click its button. Only for op action.", "type": "string" }, "alt": { - "description": "key: hold Alt with it. Default false.", + "description": "key: hold Alt with it. Default false. Only for op key.", "type": "boolean" }, "axis": { - "description": "joypad_motion: the stick or trigger axis, by name (left_x, left_y, right_x, right_y, trigger_left, trigger_right) or by its JoyAxis number.", + "description": "joypad_motion: the stick or trigger axis, by name (left_x, left_y, right_x, right_y, trigger_left, trigger_right) or by its JoyAxis number. Only for op joypad_motion.", "type": [ "string", "number" - "description": "click, mouse_click: left, right, middle, wheel_up or wheel_down, or a button number. Default left. joypad_button: the controller button, by name (a, b, x, y, back, guide, start, left_stick, right_stick, left_shoulder, right_shoulder, dpad_up, dpad_down, dpad_left, dpad_right, misc1, paddle1 to paddle4, touchpad) or by its JoyButton number; a, b, x and y are the bottom, right, left and top face buttons, as Godot names them, whatever the controller prints on them.", + "description": "click, mouse_click, mouse_drag: left, right, middle, wheel_up or wheel_down, or a button number. Default left. joypad_button: the controller button, by name (a, b, x, y, back, guide, start, left_stick, right_stick, left_shoulder, right_shoulder, dpad_up, dpad_down, dpad_left, dpad_right, misc1, paddle1 to paddle4, touchpad) or by its JoyButton number; a, b, x and y are the bottom, right, left and top face buttons, as Godot names them, whatever the controller prints on them. Only for ops click, mouse_click, mouse_drag, joypad_button.", "type": [ "string", "number" - "description": "key: hold Ctrl with it. Default false.", + "description": "key: hold Ctrl with it. Default false. Only for op key.", "type": "boolean" }, "device": { - "description": "joypad_button, joypad_motion: which controller, counting from 0. Default 0.", + "description": "joypad_button, joypad_motion: which controller, counting from 0. Default 0. Only for ops joypad_button, joypad_motion.", "minimum": 0, "type": "number" }, - "description": "click, mouse_click: default false.", + "description": "click, mouse_click: default false. Only for ops click, mouse_click.", "type": "boolean" }, + "frames": { + "description": "mouse_drag: how many process frames the stroke takes, one motion each, as a real mouse sends one a frame; a hand takes about 20 to 60 for a stroke across a screen. Only for op mouse_drag.", + "maximum": 600, + "minimum": 1, + "type": "integer" + }, "holdFrames": { - "description": "action, key, mouse_click, joypad_button: hold a whole press down for this many process frames and then let go, both inside the game, so a pour, a charge or a long press runs to a set length. A press, a frames wait and a release sent as three calls hold it for the time between the calls as well. The answer says held_frames. Not with pressed, and not on a wheel step. Default 1.", + "description": "action, key, mouse_click, joypad_button: hold a whole press down for this many process frames and then let go, both inside the game, so a pour, a charge or a long press runs to a set length. A press, a frames wait and a release sent as three calls hold it for the time between the calls as well. The answer says held_frames. Not with pressed, and not on a wheel step. Default 1. Only for ops action, key, mouse_click, joypad_button.", "maximum": 600, "minimum": 1, "type": "integer" - "description": "click: which of several controls saying the words, counting from 0 in the order a refusal lists them, best matches first. choose: the item to take, by where it is in the list, when text will not do.", + "description": "click: which of several controls saying the words, counting from 0 in the order a refusal lists them, best matches first. choose: the item to take, by where it is in the list, when text will not do. Only for ops click, choose.", "type": "number" }, "keycode": { - "description": "key: the key name, such as \"Space\" or \"A\", or its Godot keycode.", + "description": "key: the key name, such as \"Space\" or \"A\", or its Godot keycode. Only for op key.", "type": [ "string", "number" - "description": "click: the Control to click, at its centre, or the 3D node to click, where it is drawn; with says, where to look for the control instead, default the whole tree. choose: the PopupMenu, or the OptionButton or MenuButton in front of one.", + "description": "click: the Control to click, at its centre, or the 3D node to click, where it is drawn; with says, where to look for the control instead, default the whole tree. choose: the PopupMenu, or the OptionButton or MenuButton in front of one. Only for ops click, choose.", "type": "string" }, "op": { + "mouse_drag", "joypad_button", "joypad_motion" ], - "description": "action, key, mouse_click, joypad_button: leave it out and the press is a whole one, down and up a frame apart. true holds it down, false lets go of one being held. A wheel button held down is still a whole step, since a wheel is never held.", + "description": "action, key, mouse_click, joypad_button: leave it out and the press is a whole one, down and up a frame apart. true holds it down, false lets go of one being held. A wheel button held down is still a whole step, since a wheel is never held. Only for ops action, key, mouse_click, joypad_button.", "type": "boolean" }, "projectPath": { - "description": "mouse_motion: the movement the event carries. Left out, it is the distance from where the last injected pointer event put the pointer, and none for the first; that is what a control that drags reads. Give it to send a motion the position does not show.", + "description": "mouse_motion: the movement the event carries. Left out, it is the distance from where the last injected pointer event put the pointer, and none for the first; that is what a control that drags reads. Give it to send a motion the position does not show. Only for op mouse_motion.", "type": "number" }, "relativeY": { - "description": "mouse_motion: as relativeX.", + "description": "mouse_motion: as relativeX. Only for op mouse_motion.", "type": "number" }, "replace": { - "description": "text: true writes over what the field already says, which is what filling one in means. Default false types at the caret, so a field reading 2.1 typed \"0.3\" at reads 2.10.3. The answer says what the field holds afterwards either way. A read-only field is refused.", + "description": "text: true writes over what the field already says, which is what filling one in means. Default false types at the caret, so a field reading 2.1 typed \"0.3\" at reads 2.10.3. The answer says what the field holds afterwards either way. A read-only field is refused. Only for op text.", "type": "boolean" }, "says": { - "description": "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when another control says exactly the words (hidden, off the screen, covered, or on screen taking clicks without being a button), since it would stand in for that one, and when it lets the pointer through to what is under it; index presses it anyway. Words found only inside longer words (\"out\" in \"Scout\") rank after every other match and are refused unless index is given; words starting a longer word (\"Recruit\" in \"Recruits\") count. An empty field says its placeholder, and a control with no text of its own, a picture on a button, says its accessibility name. An item of an open menu is drawn by the menu rather than held as a control, so a click finds none, and the refusal names the menu and the choose call that picks it.", + "description": "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when another control says exactly the words (hidden, off the screen, covered, or on screen taking clicks without being a button), since it would stand in for that one, and when it lets the pointer through to what is under it; index presses it anyway. Words found only inside longer words (\"out\" in \"Scout\") rank after every other match and are refused unless index is given; words starting a longer word (\"Recruit\" in \"Recruits\") count. An empty field says its placeholder, and a control with no text of its own, a picture on a button, says its accessibility name. An item of an open menu is drawn by the menu rather than held as a control, so a click finds none, and the refusal names the menu and the choose call that picks it. Only for op click.", "type": "string" }, "shift": { - "description": "key: hold Shift with it. Default false.", + "description": "key: hold Shift with it. Default false. Only for op key.", "type": "boolean" }, "strength": { - "description": "action: 0 to 1. Default 1.", + "description": "action: 0 to 1. Default 1. Only for op action.", "maximum": 1, "minimum": 0, "type": "number" - "description": "text: what to type. A newline is Enter and a tab is Tab, a space is a space, and \"\" with replace empties the field. choose: the item to take, by what it says as drawn, which in a game with translations is the translation; the key it holds is matched after that. An item whose whole words are these comes first; otherwise the words match as says does anywhere, a plain word as part of the item, a glob, or alternatives split at |, enabled items ahead of disabled ones, and several matching equally are refused with each listed.", + "description": "text: what to type. A newline is Enter and a tab is Tab, a space is a space, and \"\" with replace empties the field. choose: the item to take, by what it says as drawn, which in a game with translations is the translation; the key it holds is matched after that. An item whose whole words are these comes first; otherwise the words match as says does anywhere, a plain word as part of the item, a glob, or alternatives split at |, enabled items ahead of disabled ones, and several matching equally are refused with each listed. Only for ops text, choose.", "type": "string" }, + "toX": { + "description": "mouse_drag: where the release is, in window pixels. Only for op mouse_drag.", + "type": "number" + }, + "toY": { + "description": "mouse_drag: as toX. Only for op mouse_drag.", + "type": "number" + }, "value": { - "description": "joypad_motion: where the axis is, -1 to 1: down and right are positive on a stick, and a trigger runs 0 to 1. The axis stays there until another motion moves it, as a stick held over does; send 0 to let it go back.", + "description": "joypad_motion: where the axis is, -1 to 1: down and right are positive on a stick, and a trigger runs 0 to 1. The axis stays there until another motion moves it, as a stick held over does; send 0 to let it go back. Only for op joypad_motion.", "maximum": 1, "minimum": -1, "type": "number" + "via": { + "description": "mouse_drag: points the stroke passes through on the way, in order, each [x, y]. The motions are spread along the whole stroke by distance. Default none: a straight line. Only for op mouse_drag.", + "items": { + "items": { + "type": "number" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, "x": { - "description": "mouse_click, mouse_motion: window pixels.", + "description": "mouse_click, mouse_motion: window pixels. mouse_drag: where the press is. Only for ops mouse_click, mouse_motion, mouse_drag.", "type": "number" }, "y": { - "description": "mouse_click, mouse_motion: window pixels.", + "description": "mouse_click, mouse_motion: window pixels. mouse_drag: where the press is. Only for ops mouse_click, mouse_motion, mouse_drag.", "type": "number" } },
--- pinned/runtime_inspect +++ observed/runtime_inspect { "annotations": { "openWorldHint": false, - "description": "find: a native class, matching its subclasses too, or a class_name.", + "description": "find: a native class, matching its subclasses too, or a class_name. Only for op find.", "type": "string" }, "depth": { - "description": "tree: levels to descend, 0 for the node alone. Default 3.", + "description": "tree: levels to descend, 0 for the node alone. Default 3. Only for op tree.", "minimum": 0, "type": "integer" }, - "description": "find: a group the node is in.", + "description": "find: a group the node is in. Only for op find.", "type": "string" }, "includeHidden": { - "description": "text: read hidden nodes as well, for checking that something is not showing. Default false. find: default true, since a find means the node whether or not it is drawn; false answers with what the player can actually see, which is how a panel that keeps a label for every line and hides the ones that do not is read. A node counts as hidden when the engine would not draw it: a hidden Control, Node2D or Node3D above it hides it, a plain Node in between breaks that chain as it does for the engine, and a hidden canvas layer or window hides everything in it. How many matches were left out comes back under hidden.", + "description": "text: read hidden nodes as well, for checking that something is not showing. Default false. find: default true, since a find means the node whether or not it is drawn; false answers with what the player can actually see, which is how a panel that keeps a label for every line and hides the ones that do not is read. A node counts as hidden when the engine would not draw it: a hidden Control, Node2D or Node3D above it hides it, a plain Node in between breaks that chain as it does for the engine, and a hidden canvas layer or window hides everything in it. How many matches were left out comes back under hidden. Only for ops text, find.", "type": "boolean" }, "includeProperties": { - "description": "tree: include every stored property of each node. Default false, and one row of a form answered seventy-six thousand characters with it; properties names the few that are wanted.", + "description": "tree: include every stored property of each node. Default false, and one row of a form answered seventy-six thousand characters with it; properties names the few that are wanted. Only for op tree.", "type": "boolean" }, "limit": { - "description": "find: the most nodes to answer with, default 100. text: the most lines, default 500, with omitted saying how many lines that left behind. accessibility: the most elements below the windows, depth first, with omitted counting the rest; default all. At most 5000 for either. A screen whose dialog sits under a long list is a screen read with omitted greater than zero, so raise this or point root at the dialog rather than reading the answer as what is on screen.", + "description": "find: the most nodes to answer with, default 100. text: the most lines, default 500, with omitted saying how many lines that left behind. accessibility: the most elements below the windows, depth first, with omitted counting the rest; default all. At most 5000 for either. A screen whose dialog sits under a long list is a screen read with omitted greater than zero, so raise this or point root at the dialog rather than reading the answer as what is on screen. Only for ops find, text, accessibility.", "maximum": 5000, "minimum": 1, "type": "integer" - "description": "metrics: which to read. Default all.", + "description": "metrics: which to read. Default all. Only for op metrics.", "items": { "type": "string" }, - "description": "find: a case-insensitive glob on the node name, such as \"Enemy*\", or several separated by | the way says takes them, such as \"Enemy*|Boss\", with \\| for a bar in a name. Matched against the whole name, so a bare word finds only a node called exactly that; an answer of none says how many names contain it and what glob would have found them.", + "description": "find: a case-insensitive glob on the node name, such as \"Enemy*\", or several separated by | the way says takes them, such as \"Enemy*|Boss\", with \\| for a bar in a name. Matched against the whole name, so a bare word finds only a node called exactly that; an answer of none says how many names contain it and what glob would have found them. Only for op find.", "type": "string" }, "nodePath": { - "description": "tree, find, text: where to start, default /root. rect: the node to place. property: the node to read, or an engine singleton by name as runtime_invoke takes them, \"Engine\" with property \"get_frames_drawn()\". accessibility: only the elements that node and those below it made, whether as node or part_of, each window keeping the outermost of them with what they hold, and matched counting them. What a ScrollContainer holds out of its sight is not in the tree until it is scrolled to, so an element missing under one may be scrolled away rather than hidden.", + "description": "tree, find, text: where to start, default /root. rect: the node to place. property: the node to read, or an engine singleton by name as runtime_invoke takes them, \"Engine\" with property \"get_frames_drawn()\". accessibility: only the elements that node and those below it made, whether as node or part_of, each window keeping the outermost of them with what they hold, and matched counting them. What a ScrollContainer holds out of its sight is not in the tree until it is scrolled to, so an element missing under one may be scrolled away rather than hidden. Only for ops tree, text, find, rect, property, accessibility.", "type": "string" }, "op": { - "description": "tree: the properties to read off each node, by name, so the text of every label and button under a panel is one call: a node that has not got one leaves it out. Colons read through what a node holds, as property does for find. accessibility: the fields to keep on each element, such as [\"role\", \"name\", \"node\"], leaving out the rest (bounds, actions, tooltip); children are always kept.", + "description": "tree: the properties to read off each node, by name, so the text of every label and button under a panel is one call: a node that has not got one leaves it out. Colons read through what a node holds, as property does for find. accessibility: the fields to keep on each element, such as [\"role\", \"name\", \"node\"], leaving out the rest (bounds, actions, tooltip); children are always kept. Only for ops tree, accessibility.", "items": { "type": "string" }, - "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: \"size:x\", \"global_position:y\", \"modulate:a\", \"global_transform:origin:z\"; a name the value does not have is refused, naming the ones it has. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.", + "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: \"size:x\", \"global_position:y\", \"modulate:a\", \"global_transform:origin:z\"; a name the value does not have is refused, naming the ones it has. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead. Only for ops property, find.", "type": "string" }, "says": { - "description": "accessibility: only the elements whose name or value says these words, matched as find matches them, with what they hold. find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words.", + "description": "accessibility: only the elements whose name or value says these words, matched as find matches them, with what they hold. find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words. Only for ops find, accessibility.", "type": "string" }, "script": { - "description": "find: the script file the node carries.", + "description": "find: the script file the node carries. Only for op find.", "type": "string" }, "timeoutMs": {
--- pinned/runtime_invoke +++ observed/runtime_invoke { "annotations": { "destructiveHint": true, - "description": "call: the arguments, fitted to the method's parameter types. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused.", + "description": "call: the arguments, fitted to the method's parameter types. A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused. Only for op call.", "type": "array" }, "method": { - "description": "call: which one to call. Colons call through what a node holds, \"_game:run:advance\", the same way a property is written through them, a list or a map is stepped into by index or key, \"_game:run:roster:0:retire\", and a step written as a call walks through what a reading method returned, \"get_viewport():gui_get_focus_owner\", as runtime_inspect property describes.", + "description": "call: which one to call. Colons call through what a node holds, \"_game:run:advance\", the same way a property is written through them, a list or a map is stepped into by index or key, \"_game:run:roster:0:retire\", and a step written as a call walks through what a reading method returned, \"get_viewport():gui_get_focus_owner\", as runtime_inspect property describes. Only for op call.", "type": "string" }, "nodePath": { - "description": "Absolute node path, such as \"/root/Main/Player\", or the name of an engine singleton: Engine, Performance, RenderingServer, AudioServer, PhysicsServer2D, PhysicsServer3D, NavigationServer2D, NavigationServer3D, Time or TranslationServer, so \"RenderingServer\" with method viewport_get_measured_render_time_cpu reads what the engine measures. The other singletons are refused: OS runs programs, DisplayServer switches the keyboard layout and Input moves the real pointer.", + "description": "Absolute node path, such as \"/root/Main/Player\", or the name of an engine singleton: Engine, Performance, RenderingServer, AudioServer, PhysicsServer2D, PhysicsServer3D, NavigationServer2D, NavigationServer3D, Time or TranslationServer, so \"RenderingServer\" with method viewport_get_measured_render_time_cpu reads what the engine measures. The other singletons are refused: OS runs programs, DisplayServer switches the keyboard layout and Input moves the real pointer. Only for ops set, call.", "type": "string" }, "op": { - "description": "call: read the object the call answered with in the same answer, under result_properties: true for every variable its script declares, or a list of names, each a colon path walked from that object the way property paths are (\"dead_allowed\", \"terms:0:name\"). A name that is not there is listed under result_properties_missing with why, and a result that is not an object answers properties_note instead. Without it an object comes back as its class and id only.", + "description": "call: read the object the call answered with in the same answer, under result_properties: true for every variable its script declares, or a list of names, each a colon path walked from that object the way property paths are (\"dead_allowed\", \"terms:0:name\"). A name that is not there is listed under result_properties_missing with why, and a result that is not an object answers properties_note instead. Without it an object comes back as its class and id only. Only for op call.", "items": { "type": "string" }, - "description": "set: which one to write. Colons write through what a node holds, \"_game:run:day\", and a number or a key steps into a list or a map on the way, \"_game:run:roster:0:name\", a negative number counting from the end, and a step written as a call, \"get_viewport():gui_embed_subwindows\", walks through what a reading method returned, as runtime_inspect property describes. A map keyed by numbers is stepped into by the number, \"_game:units:3\". A component of a vector, a colour or a transform, \"position:x\", writes that one alone: the value holding it is a copy, so it is put back where it was read from, setter and all, and a component of what a call returned is refused, since the game keeps that copy nowhere. The answer reads back off the same holder, and a write that leaves the property as it was is refused and says what it still holds: the engine not taking it and a setter keeping the value look the same, so the refusal names neither. A write of the value the property already holds, at its own precision, as a float given in 64 bits to a property keeping 32, is not refused: it answers unchanged: true.", + "description": "set: which one to write. Colons write through what a node holds, \"_game:run:day\", and a number or a key steps into a list or a map on the way, \"_game:run:roster:0:name\", a negative number counting from the end, and a step written as a call, \"get_viewport():gui_embed_subwindows\", walks through what a reading method returned, as runtime_inspect property describes. A map keyed by numbers is stepped into by the number, \"_game:units:3\". A component of a vector, a colour or a transform, \"position:x\", writes that one alone: the value holding it is a copy, so it is put back where it was read from, setter and all, and a component of what a call returned is refused, since the game keeps that copy nowhere. The answer reads back off the same holder, and a write that leaves the property as it was is refused and says what it still holds: the engine not taking it and a setter keeping the value look the same, so the refusal names neither. A write of the value the property already holds, at its own precision, as a float given in 64 bits to a property keeping 32, is not refused: it answers unchanged: true. Only for op set.", "type": "string" }, "requestId": { - "description": "result: the requestId a pending answer gave.", + "description": "result: the requestId a pending answer gave. Only for op result.", "minimum": 1, "type": "integer" }, - "description": "set, call: how long to wait for the answer before answering pending with a requestId, which does not stop the call. Default 10000, or GDHARNESS_RUNTIME_TIMEOUT_MS. Give a longer one for a call known to take a while, such as one that plays many turns in one go.", + "description": "set, call: how long to wait for the answer before answering pending with a requestId, which does not stop the call. Default 10000, or GDHARNESS_RUNTIME_TIMEOUT_MS. Give a longer one for a call known to take a while, such as one that plays many turns in one go. Only for ops set, call.", "minimum": 1, "type": "integer" }, - "description": "set: the value, fitted to the property's type. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}, which is the form a read answers with. \"\" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name." + "description": "set: the value, fitted to the property's type. A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}, which is the form a read answers with. \"\" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name. Only for op set." } }, "required": [
--- pinned/runtime_wait +++ observed/runtime_wait { "annotations": { "openWorldHint": false, - "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal (needs nodePath, signal): wait for a signal and answer with what it carried; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found.", + "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal (needs nodePath, signal): wait for a signal and answer with what it carried; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found; one already true when asked answers at once, with frames 0.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "until, with a property: how what the property reads stands to value before the wait is met. Default ==. != waits for it to read as anything else. >=, <=, > and < put a number in order, met on the first look past the value or level with it at the precision the property keeps, which is how a counter that steps rather than lands is waited on: a day counter on a fast clock went from 31 to 34 between two looks, and a wait for 32 ran out. For these the value is the number it is rather than fitted to the property, so a fraction on a whole-number property is a threshold rather than refused, and a property holding anything but a number is refused.", + "description": "until, with a property: how what the property reads stands to value before the wait is met. Default ==. != waits for it to read as anything else. >=, <=, > and < put a number in order, met on the first look past the value or level with it at the precision the property keeps, which is how a counter that steps rather than lands is waited on: a day counter on a fast clock went from 31 to 34 between two looks, and a wait for 32 ran out. For these the value is the number it is rather than fitted to the property, so a fraction on a whole-number property is a threshold rather than refused, and a property holding anything but a number is refused. Only for op until.", "enum": [ "==", "!=", - "description": "frames: how many to let pass, 1 to 600. More than that is refused.", + "description": "frames: how many to let pass, 1 to 600. More than that is refused. Only for op frames.", "maximum": 600, "minimum": 1, "type": "integer" - "description": "until, with says: count words on a hidden node too. Default false, since a wait for words is a wait for them to be shown: a button that exists hidden through a whole animation carries its words the whole time and satisfied the wait at once, and every wait on that screen fell back to counting frames.", + "description": "until, with says: count words on a hidden node too. Default false, since a wait for words is a wait for them to be shown: a button that exists hidden through a whole animation carries its words the whole time and satisfied the wait at once, and every wait on that screen fell back to counting frames. Only for op until.", "type": "boolean" }, "nodePath": { - "description": "signal, until: the node. Needed for a signal and for a property; for says it defaults to /root, the whole screen.", + "description": "signal, until: the node. Needed for a signal and for a property; for says it defaults to /root, the whole screen. Only for ops signal, until.", "type": "string" }, "op": { - "description": "until: the property name, or a colon path through what the node holds, \"_game:run:day\", read the way runtime_inspect property reads one, a step written as a call included, and one axis of a vector as well: \"global_position:x\" with compare \">=\" stops once a thing has travelled far enough. Walked again on every frame, so a holder the game replaces while the wait is on is followed.", + "description": "until: the property name, or a colon path through what the node holds, \"_game:run:day\", read the way runtime_inspect property reads one, a step written as a call included, and one axis of a vector as well: \"global_position:x\" with compare \">=\" stops once a thing has travelled far enough. Walked again on every frame, so a holder the game replaces while the wait is on is followed. Only for op until.", "type": "string" }, "says": { - "description": "until: wait for these words to appear anywhere under nodePath, or anywhere in the game when no nodePath is given, instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Matched the way runtime_inspect find matches says: case-insensitive, a plain word anywhere in the text, a pattern with * or ? a glob over the whole of it, and alternatives separated by | met by whichever appears first (\"*won*|*lost*\"); on a node the player can see unless includeHidden says otherwise; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property. It looks every frame while a look is cheap and less often on a screen large enough for a look to cost a real share of a frame, so the game keeps its speed; the answer counts the frames the wait spanned and the looks it took, and says under looking_ms how long the looks took between them.", + "description": "until: wait for these words to appear anywhere under nodePath, or anywhere in the game when no nodePath is given, instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Matched the way runtime_inspect find matches says: case-insensitive, a plain word anywhere in the text, a pattern with * or ? a glob over the whole of it, and alternatives separated by | met by whichever appears first (\"*won*|*lost*\"); on a node the player can see unless includeHidden says otherwise; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property. It looks every frame while a look is cheap and less often on a screen large enough for a look to cost a real share of a frame, so the game keeps its speed; the answer counts the frames the wait spanned and the looks it took, and says under looking_ms how long the looks took between them. Only for op until.", "type": "string" }, "signal": { - "description": "signal: the signal name.", + "description": "signal: the signal name. Only for op signal.", "type": "string" }, "timeoutMs": { - "description": "signal, until: how long to wait before answering anyway, 1 to 120000. Default 5000.", + "description": "signal, until: how long to wait before answering anyway, 1 to 120000. Default 5000. Only for ops signal, until.", "maximum": 120000, "minimum": 1, "type": "integer" - "description": "until: the value to wait for, fitted to the property's type and compared at the precision the property keeps, so -1.1 meets a 32-bit float holding -1.10000002. A value the property would have to cut is refused: a fraction on a whole number, a number other than 0 or 1 on a bool. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it." + "description": "until: the value to wait for, fitted to the property's type and compared at the precision the property keeps, so -1.1 meets a 32-bit float holding -1.10000002. A value the property would have to cut is refused: a fraction on a whole number, a number other than 0 or 1 on a bool. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it. Only for op until." } }, "required": [
--- pinned/scene_animation +++ observed/scene_animation { "annotations": { "destructiveHint": false, - "description": "connect_states: the condition parameter that advances.", + "description": "connect_states: the condition parameter that advances. Only for op connect_states.", "type": "string" }, "animTreePath": { - "description": "The AnimationTree node.", + "description": "The AnimationTree node. Only for ops read_states, add_state, connect_states.", "type": "string" }, "animationName": { - "description": "The animation in the player. read: the one to answer with whole; without it, read lists them. add_state: the one that state plays.", + "description": "The animation in the player. read: the one to answer with whole; without it, read lists them. add_state: the one that state plays. Only for ops read, create, add_track, add_state.", "type": "string" }, "fromState": { - "description": "connect_states: the state the transition leaves.", + "description": "connect_states: the state the transition leaves. Only for op connect_states.", "type": "string" }, "length": { - "description": "create: seconds. Default 1.", + "description": "create: seconds. Default 1. Only for op create.", "type": "number" }, "loopMode": { - "description": "create: default none.", + "description": "create: default none. Only for op create.", "enum": [ "none", "linear", - "description": "The AnimationPlayer node.", + "description": "The AnimationPlayer node. Only for ops read, create, add_track.", "type": "string" }, "projectPath": { - "description": "A nested state machine, by the names of the states that lead to it. Default the root.", + "description": "A nested state machine, by the names of the states that lead to it. Default the root. Only for ops read_states, add_state, connect_states.", "type": "string" }, "stateName": { - "description": "add_state: the state to add.", + "description": "add_state: the state to add. Only for op add_state.", "type": "string" }, "step": { - "description": "create: keyframe snap in seconds. Default 0.1.", + "description": "create: keyframe snap in seconds. Default 0.1. Only for op create.", "type": "number" }, "toState": { - "description": "connect_states: the state it arrives at.", + "description": "connect_states: the state it arrives at. Only for op connect_states.", "type": "string" }, "track": { - "description": "The track to add.", + "description": "The track to add. Only for op add_track.", "properties": { "keyframes": { "items": { - "description": "connect_states: default immediate.", + "description": "connect_states: default immediate. Only for op connect_states.", "enum": [ "immediate", "sync",
--- pinned/scene_create +++ observed/scene_create { "annotations": { "destructiveHint": true, - "description": "Creates a scene file where none is, saves one, or saves a copy under a new path. A save of a scene open in the editor saves the editor's copy, unsaved changes included. Needs the editor connected. Operations: create: a new scene with one root node; save: save the scene as it is in the editor; save_as (needs newPath): save a copy under newPath. Default create.", + "description": "Creates a scene file where none is, saves one, or saves a copy under a new path. A save of a scene open in the editor saves the editor's copy, unsaved changes included. Needs the editor connected. Operations: create: a new scene with one root node; save: save the scene as it is in the editor, or as the file loads when the editor does not have it open; from says which; save_as (needs newPath): save a copy under newPath. Default create.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "save_as: where the copy goes.", + "description": "save_as: where the copy goes. Only for op save_as.", "type": "string" }, "op": { - "description": "create: the root node name. Default the class name, as the editor names a new root.", + "description": "create: the root node name. Default the class name, as the editor names a new root. Only for op create.", "type": "string" }, "rootNodeType": { - "description": "create: the root node class. Default Node.", + "description": "create: the root node class. Default Node. Only for op create.", "type": "string" }, "scenePath": { - "description": "create: a script to attach to the root.", + "description": "create: a script to attach to the root. Only for op create.", "type": "string" } },
--- pinned/scene_node +++ observed/scene_node { "annotations": { "destructiveHint": true, - "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Needs the editor connected. Operations: add (needs nodeType, nodeName): add a node of any class; get (needs nodePath): read a node's properties; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", + "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. set with name renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Needs the editor connected. Operations: add (needs nodeName): add a node of any class, or an instance of a scene; get (needs nodePath): read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent, or to another place among its siblings; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", "inputSchema": { "additionalProperties": false, "properties": { + "index": { + "description": "add and reparent: where the node goes among the parent's children, 0 for first. Default last. Child order is draw order in a CanvasItem and layout order in a Container. Only for ops add, reparent.", + "minimum": 0, + "type": "integer" + }, + "instancePath": { + "description": "add: a scene to instance as the new node instead of a class, such as \"ui/panel.tscn\". It is saved as an instance of that file, the way the editor's Instantiate Child Scene saves one, so changes to that scene reach it; properties set its root. A scene that is the target or instances it is refused. Only for op add.", + "type": "string" + }, "layer": { "description": "set_tilemap_cells: the layer of a TileMap. Default 0. A TileMapLayer is one layer.", "type": "number" - "description": "reparent: the new parent.", + "description": "reparent: the new parent, which may be the one it has, to move it among its siblings.", "type": "string" }, "nodeName": { - "description": "add: the node class, such as \"CharacterBody2D\".", + "description": "add: the node class, such as \"CharacterBody2D\". Give this or instancePath.", "type": "string" }, "op": { - "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds.", + "description": "Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {\"x\": 1, \"y\": 2} or tagged {\"_type\": \"Vector2\", \"x\": 1, \"y\": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: \"position:x\", \"modulate:a\", \"region_rect:size:y\"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as \"../Player\". Each value is read back once set, and one the engine changed or refused is refused with what it holds.", "type": "object" }, "scenePath": {
--- pinned/scene_signal +++ observed/scene_signal { "annotations": { "destructiveHint": true, - "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. Needs the editor connected. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", + "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. Needs the editor connected. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/scene_tree +++ observed/scene_tree { "annotations": { "openWorldHint": false, - "description": "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected. For the nodes of a game that is running, runtime_inspect tree, which needs no editor.", + "description": "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/script_edit +++ observed/script_edit { "annotations": { "destructiveHint": true, - "description": "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, of which the newest ten are kept, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so. Operations: create: a new script file; modify (needs modifications): add to an existing script; rename (needs symbol, newName): rename a class or a member wherever the engine uses it, across the project.", + "description": "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written; with an editor serving the project it reloads the script there too, so the editor's answers read what was written, and editorReloaded says so (editorNote when it could not, or when a script tab holds unsaved edits that saving would write over the file). rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, of which the newest ten are kept, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so. Operations: create: a new script file; modify (needs modifications): add to an existing script; rename (needs symbol, newName): rename a class or a member wherever the engine uses it, across the project.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "Additions to make, in order.", + "description": "Additions to make, in order. A name the script already declares at the top level, or one the call adds twice, refuses the call before anything is written.", "items": { "additionalProperties": false, "properties": { - "description": "Variables: the initial value, as written in GDScript.", + "description": "Variables: the initial value. Constants: the value, which a constant needs. As written in GDScript.", "type": "string" }, "exportHint": { - "description": "Functions: where to insert.", + "description": "Functions: where to insert. end, the default, is the end of the file; after_ready and after_init are after _ready or _init, above the comments and annotation lines that open the function after it, and the end when nothing follows. A function goes in with two blank lines on each side, as gdformat writes them.", "enum": [ "end", "after_ready", - "add_signal" + "add_signal", + "add_constant" ], "type": "string" }, - "description": "Variables: the type annotation.", + "description": "Variables and constants: the type annotation; without one, the type the value evaluates to.", "type": "string" } }, - "description": "rename: the new name.", + "description": "rename: the new name. Only for op rename.", "type": "string" }, "newScriptPath": { - "description": "rename: also move the declaring script here, with its .uid beside it, rewriting every string that names it by path in the same style it was written (res:// or relative), and the relative paths in the moved script itself.", + "description": "rename: also move the declaring script here, with its .uid beside it, rewriting every string that names it by path in the same style it was written (res:// or relative), and the relative paths in the moved script itself. Only for op rename.", "type": "string" }, "op": { - "description": "rename: answer with what would change and what would be left alone, and write nothing. Default false.", + "description": "rename: answer with what would change and what would be left alone, and write nothing. Default false. Only for op rename.", "type": "boolean" }, "projectPath": { - "description": "rename: what to rename, declared by the script at scriptPath: its class_name, or a method, variable, constant, signal, enum, enum value or inner class at its top level. A class is renamed from the files and needs no editor: every bare use in code, the declaration, doc links such as [Told] and [method Told.say], and the script_class a .tres records. A script that declares something of its own by the old name, and a use after a dot, are left alone, since which of those mean the class cannot be told from the text. A member is renamed from what the editor's language server resolves to it, so it needs the editor open on this project; a method is renamed together with every override of it in the classes inheriting it, and from the topmost class declaring it when scriptPath's is itself an override. Doc links naming the member through any class inheriting it are renamed too, and so are a scene's or resource's saved value of a variable on a node or resource whose script inherits it, and a connection naming the signal on its emitter or the method on its receiver. A name the engine itself declares on the native base, such as _ready, is refused, as is a new name already taken: by a class, an autoload, an identifier in code, a native class or built-in type, or a member anywhere in the class's chain.", + "description": "rename: what to rename, declared by the script at scriptPath: its class_name, or a method, variable, constant, signal, enum, enum value or inner class at its top level. A class is renamed from the files and needs no editor: every bare use in code, the declaration, doc links such as [Told] and [method Told.say], and the script_class a .tres records. A script that declares something of its own by the old name, and a use after a dot, are left alone, since which of those mean the class cannot be told from the text. A member is renamed from what the editor's language server resolves to it, so it needs the editor open on this project; a method is renamed together with every override of it in the classes inheriting it, and from the topmost class declaring it when scriptPath's is itself an override. Doc links naming the member through any class inheriting it are renamed too, and so are a scene's or resource's saved value of a variable on a node or resource whose script inherits it, and a connection naming the signal on its emitter or the method on its receiver. A name the engine itself declares on the native base, such as _ready, is refused, as is a new name already taken: by a class, an autoload, an identifier in code, a native class or built-in type, or a member anywhere in the class's chain. Only for op rename.", "type": "string" }, "template": {
Changed 2026-10-15 11:14 UTC
6 descriptions rewritten, 3 schema or annotation changes with the description left byte-identical.
--- pinned/debug_breakpoint +++ observed/debug_breakpoint { "annotations": { "destructiveHint": true, - "description": "Sets or removes a breakpoint through the editor's debug adapter. Needs the editor, not a running game: set them first, then editor_run, and the game stops where you asked. The editor keeps a breakpoint set this way for one play, so every breakpoint this server holds is sent again before each play editor_run starts, and the start answer lists them under breakpoints. The set is also kept in the project, for the server that comes after a reconnect: a breakpoint stays until it is removed here, whichever server set it. Every answer lists the whole set under held, and the breakpoints the user set in the editor's own gutter under setInEditor; setting one here never takes those away, and removing a line here clears it whoever set it. Godot clears every breakpoint in the editor when a debug session opens unless network/debug_adapter/sync_breakpoints is on, which the addon turns on as it loads; an editor running an older addon is said so under breakpointsAtRisk, here and in editor_status. Operations: set: set a breakpoint; remove: remove a breakpoint.", + "description": "Sets or removes a breakpoint. Set them first, then editor_run, and the game stops where you asked. With the editor connected it goes through the editor's debug adapter; with none, it is held here. A game this server starts itself, which editor_run start does for a run given args, savesIn, env or silent, or with no editor, is handed every breakpoint held here as it starts, and one set while that game runs reaches it at once (runningGame says so). The editor keeps a breakpoint set this way for one play, so every breakpoint this server holds is sent again before each play editor_run starts, and the start answer lists them under breakpoints. The set is also kept in the project, for the server that comes after a reconnect: a breakpoint stays until it is removed here, whichever server set it. Every answer lists the whole set under held, and the breakpoints the user set in the editor's own gutter under setInEditor; setting one here never takes those away, and removing a line here clears it whoever set it. Godot clears every breakpoint in the editor when a debug session opens unless network/debug_adapter/sync_breakpoints is on, which the addon turns on as it loads; an editor running an older addon is said so under breakpointsAtRisk, here and in editor_status. Operations: set: set a breakpoint; remove: remove a breakpoint.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/debug_control +++ observed/debug_control { "annotations": { "destructiveHint": false, - "description": "Continues or steps the debugged game through the editor's debug adapter. A step waits for the game to stop again and answers with the stack where it ended up; one that has not stopped the game again within ten seconds answers heldAgain false and no stack. There is no pause and no step_out: Godot's adapter answers a pause by reporting the game stopped and leaving it running, and implements no stepOut at all, so hold the game where you want it with a breakpoint and step over or into from there. Operations: continue: resume after a breakpoint; step_over: run the current line; step_into: run the current line, stopping inside whatever it calls.", + "description": "Pauses, continues or steps the debugged game. A step waits for the game to stop again and answers with the stack where it ended up; one that has not stopped the game again within ten seconds answers heldAgain false and no stack. A game the editor plays is moved through the editor's debug adapter, which cannot pause or step out: it answers a pause by reporting the game stopped and leaving it running, and implements no stepOut, so hold such a game with a breakpoint and step over or into from there. A game this server started itself has its keeper as its debugger, which does both: pause breaks into the game wherever it is, a script spinning in a loop included, which is how a game hung on its first frame is asked where it is. Operations: pause: stop a game this server started where it is, answering with the stack. The game takes it between frames, and in a script every 2048 lines it runs, so a loop that sleeps between few lines can take seconds; the call waits thirty, and past that answers paused false with the request still standing. Stopped between frames, where no script runs, the game is stepped on to the next line of script it runs; continue: resume after a breakpoint; step_over: run the current line; step_into: run the current line, stopping inside whatever it calls; step_out: run to the end of the current function and stop in its caller; a game this server started only.", "inputSchema": { "additionalProperties": false, "properties": { + "pause", "continue", "step_over", - "step_into" + "step_into", + "step_out" ], "type": "string" }
--- pinned/debug_state +++ observed/debug_state { "annotations": { "openWorldHint": false, - "description": "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the debug adapter's console output so far, its last thousand lines, with any earlier ones counted under notShown. A stop is reported by the editor's adapter only to the sessions connected when it happened, so a server that connected afterwards, which is every server after a reconnect, is shown a thread and no frames about a game still sitting at its breakpoint. That server asks the runtime instead: a held game accepts a connection and never answers, and the refusal then says held rather than running, and that the stack cannot be read from here. debug_control continue lets it go, and the next stop is one this session is told about, with its stack. Operations: stack: the stack trace; variables: locals, members and globals at a frame, with their values; output: console output captured through the debug adapter. Default stack.", + "description": "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the debugger's console output so far, its last thousand lines, with any earlier ones counted under notShown. For a game this server started, its keeper's debugger answers: each frame names its script as the project spells it under script and on this machine under path, and held says which thread stopped and why. For a game the editor plays, the editor's debug adapter answers. A stop is reported by the editor's adapter only to the sessions connected when it happened, so a server that connected afterwards, which is every server after a reconnect, is shown a thread and no frames about a game still sitting at its breakpoint. That server asks the runtime instead: a held game accepts a connection and never answers, and the refusal then says held rather than running, and that the stack cannot be read from here. debug_control continue lets it go, and the next stop is one this session is told about, with its stack. Operations: stack: the stack trace; variables: locals, members and globals at a frame, with their values; output: console output captured through the debug adapter. Default stack.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/editor_rescan +++ observed/editor_rescan { "annotations": { "destructiveHint": false, - "description": "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it, and the answer says how long under waitedForEditorScanMs. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. The walk also passes over a file whose .uid alone changed, so the editor keeps the old UID and every engine reading its cache warns of a duplicate; before scanning, each file whose .uid names a UID the editor does not hold for it is read again and named under uidsReread, and files on disk naming one UID between them are named under uidsDuplicatedOnDisk, which reading again cannot settle. A class the scan brings in is named under broughtIn, and every script naming it is reloaded in the editor and named under dependentsReloaded: what the editor compiled from them while it could not see the class stays compiled that way after the scan, and a script extending a new base class went on reporting \"Could not find base class\" until reloaded. One that would not reload is named under dependentsNotReloaded and the call is not ok, as it is not when the script named in reloadScript was not reloaded. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.", + "description": "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it, and the answer says how long under waitedForEditorScanMs. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. The walk also passes over a file whose .uid alone changed, so the editor keeps the old UID and every engine reading its cache warns of a duplicate; before scanning, each file whose .uid names a UID the editor does not hold for it is read again and named under uidsReread, and files on disk naming one UID between them are named under uidsDuplicatedOnDisk, which reading again cannot settle. A class the scan brings in is named under broughtIn, and every script naming it is reloaded in the editor and named under dependentsReloaded: what the editor compiled from them while it could not see the class stays compiled that way after the scan, and a script extending a new base class went on reporting \"Could not find base class\" until reloaded. One that would not reload is named under dependentsNotReloaded and the call is not ok, as it is not when the script named in reloadScript was not reloaded. An editor importing answers nothing until the import is over, and a hidden one imports on the thread that answers, so a large addon install can leave the rescan itself unanswered: that is waited for as any busy editor is, within timeoutMs, and when the wait runs out first the answer is stillWorking. The classes the editor could not see before that scan are then noted in the project, and the next editor_rescan of the same editor, from any server, names them under broughtIn and reloads what names them, which it could not otherwise know to do, since by then the editor sees them. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/editor_run +++ observed/editor_run { "annotations": { "destructiveHint": true, - "description": "The game's own arguments, what OS.get_cmdline_user_args() answers, such as [\"--level=2\"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so the debug_* tools do not answer for it.", + "description": "The game's own arguments, what OS.get_cmdline_user_args() answers, such as [\"--level=2\"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so its debugger is the keeper holding it rather than the editor; the debug_* tools answer for it all the same.", "items": { "type": "string" }, + "executable": { + "description": "start: an exported build to run instead of the project, as a path inside the project such as export/windows/game.exe, so what a player runs can be measured. It takes args, engineArgs, savesIn, env, headless and visible as the project does, goes on the hidden desktop the same way, and is ended by stop, but takes no scene (its pack holds the main scene) and no profile, and has no debugger: a release build quits when given one. Its runtime is not waited for.", + "type": "string" + }, "frames": { "description": "check: frames to run before quitting. Default 3.", "minimum": 1, - "description": "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, and the debug_* tools do not answer for it. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before.", + "description": "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, whose keeper is its debugger rather than the editor. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before.", "type": "boolean" }, "timeoutMs": {
--- pinned/runtime_input +++ observed/runtime_input { "annotations": { "destructiveHint": false, + "holdFrames": { + "description": "action, key, mouse_click, joypad_button: hold a whole press down for this many process frames and then let go, both inside the game, so a pour, a charge or a long press runs to a set length. A press, a frames wait and a release sent as three calls hold it for the time between the calls as well. The answer says held_frames. Not with pressed, and not on a wheel step. Default 1.", + "maximum": 600, + "minimum": 1, + "type": "integer" + }, "index": { "description": "click: which of several controls saying the words, counting from 0 in the order a refusal lists them, best matches first. choose: the item to take, by where it is in the list, when text will not do.", "type": "number"
--- pinned/runtime_inspect +++ observed/runtime_inspect { "annotations": { "openWorldHint": false, - "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, the performance metrics, or what it tells a screen reader. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count; accessibility: what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role as UI Automation names it (text for static text, button, check_box, group for a control holding others), its name, its value where it has one, its bounds in the window's pixels, the actions a screen reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one holding the focus. The name is what is read aloud, so an empty one is read as nothing; static text takes it from DisplayServer.accessibility_update_set_value, not from a name set on it, and the answer says under _note how many text elements on screen have none. With no control focused, the engine reports the first node under the root as focused, which in a run with the runtime is GdharnessRuntime. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or with the project's accessibility/general/accessibility_support at Always Active, since the engine builds the tree only when asked; reading it takes a second or two, starting PowerShell inside the game so it reaches the game's own desktop. Default tree.", + "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, the performance metrics, or what it tells a screen reader. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. physics_steps_per_second is the physics steps taken a second over those frames, fewer than physics_ticks_per_second when a frame needs more than max_physics_steps_per_frame or the editor's Game view slows it. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count; accessibility: what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role (text for static text, button, check_box, group for a control holding others), name, value, description, placeholder, bounds in the window's pixels, the actions a reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one with the focus. node is the node that made the element; an element a node made as a part of itself has part_of, the node drawn around it that holds its words, or part_of_candidates where several are, and part: announcement marks the text a window announces through. An empty name is read as nothing: static text takes it from accessibility_update_set_value, and _note counts the text elements on screen with none. tooltip is the node's tooltip_text, which a Windows reader is not told. With nothing focused, the engine calls GdharnessRuntime focused. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or accessibility_support at Always Active, since the engine builds the tree only when asked; reading takes a second or two. Default tree.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "find: the most nodes to answer with, default 100. text: the most lines, default 500, with omitted saying how many lines that left behind. At most 5000 for either. A screen whose dialog sits under a long list is a screen read with omitted greater than zero, so raise this or point root at the dialog rather than reading the answer as what is on screen.", + "description": "find: the most nodes to answer with, default 100. text: the most lines, default 500, with omitted saying how many lines that left behind. accessibility: the most elements below the windows, depth first, with omitted counting the rest; default all. At most 5000 for either. A screen whose dialog sits under a long list is a screen read with omitted greater than zero, so raise this or point root at the dialog rather than reading the answer as what is on screen.", "maximum": 5000, "minimum": 1, "type": "integer" - "description": "tree, find, text: where to start, default /root. rect: the node to place. property: the node to read.", + "description": "tree, find, text: where to start, default /root. rect: the node to place. property: the node to read, or an engine singleton by name as runtime_invoke takes them, \"Engine\" with property \"get_frames_drawn()\". accessibility: only the elements that node and those below it made, whether as node or part_of, each window keeping the outermost of them with what they hold, and matched counting them. What a ScrollContainer holds out of its sight is not in the tree until it is scrolled to, so an element missing under one may be scrolled away rather than hidden.", "type": "string" }, "op": { - "description": "tree: the properties to read off each node, by name, so the text of every label and button under a panel is one call: a node that has not got one leaves it out. Colons read through what a node holds, as property does for find.", + "description": "tree: the properties to read off each node, by name, so the text of every label and button under a panel is one call: a node that has not got one leaves it out. Colons read through what a node holds, as property does for find. accessibility: the fields to keep on each element, such as [\"role\", \"name\", \"node\"], leaving out the rest (bounds, actions, tooltip); children are always kept.", "items": { "type": "string" }, - "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.", + "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: \"size:x\", \"global_position:y\", \"modulate:a\", \"global_transform:origin:z\"; a name the value does not have is refused, naming the ones it has. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.", "type": "string" }, "says": { - "description": "find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words.", + "description": "accessibility: only the elements whose name or value says these words, matched as find matches them, with what they hold. find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words.", "type": "string" }, "script": {
--- pinned/runtime_invoke +++ observed/runtime_invoke { "annotations": { "destructiveHint": true, - "description": "Sets a property or calls a method on a node in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors and warnings the engine reported while a called method ran come back under errors, the first 20, with errors_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes. Operations: set (needs nodePath, property, value): set a property, on a node or on an object it holds; call (needs nodePath, method): call a method, on a node or on an object it holds, and return its result; result (needs requestId): collect the reply to a set or call whose wait ran out, by the requestId it answered with.", + "description": "Sets a property or calls a method on a node, or on one of the engine singletons nodePath names, in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors and warnings the engine reported while a called method ran come back under errors, the first 20, with errors_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes. Operations: set (needs nodePath, property, value): set a property, on a node or on an object it holds; call (needs nodePath, method): call a method, on a node or on an object it holds, and return its result; result (needs requestId): collect the reply to a set or call whose wait ran out, by the requestId it answered with.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "Absolute node path, such as \"/root/Main/Player\".", + "description": "Absolute node path, such as \"/root/Main/Player\", or the name of an engine singleton: Engine, Performance, RenderingServer, AudioServer, PhysicsServer2D, PhysicsServer3D, NavigationServer2D, NavigationServer3D, Time or TranslationServer, so \"RenderingServer\" with method viewport_get_measured_render_time_cpu reads what the engine measures. The other singletons are refused: OS runs programs, DisplayServer switches the keyboard layout and Input moves the real pointer.", "type": "string" }, "op": { - "description": "set: which one to write. Colons write through what a node holds, \"_game:run:day\", and a number or a key steps into a list or a map on the way, \"_game:run:roster:0:name\", a negative number counting from the end, and a step written as a call, \"get_viewport():gui_embed_subwindows\", walks through what a reading method returned, as runtime_inspect property describes. A map keyed by numbers is stepped into by the number, \"_game:units:3\". The answer reads back off the same holder, and a write that leaves the property as it was is refused and says what it still holds: the engine not taking it and a setter keeping the value look the same, so the refusal names neither. A write of the value the property already holds, at its own precision, as a float given in 64 bits to a property keeping 32, is not refused: it answers unchanged: true.", + "description": "set: which one to write. Colons write through what a node holds, \"_game:run:day\", and a number or a key steps into a list or a map on the way, \"_game:run:roster:0:name\", a negative number counting from the end, and a step written as a call, \"get_viewport():gui_embed_subwindows\", walks through what a reading method returned, as runtime_inspect property describes. A map keyed by numbers is stepped into by the number, \"_game:units:3\". A component of a vector, a colour or a transform, \"position:x\", writes that one alone: the value holding it is a copy, so it is put back where it was read from, setter and all, and a component of what a call returned is refused, since the game keeps that copy nowhere. The answer reads back off the same holder, and a write that leaves the property as it was is refused and says what it still holds: the engine not taking it and a setter keeping the value look the same, so the refusal names neither. A write of the value the property already holds, at its own precision, as a float given in 64 bits to a property keeping 32, is not refused: it answers unchanged: true.", "type": "string" }, "requestId": {
--- pinned/runtime_wait +++ observed/runtime_wait { "annotations": { "openWorldHint": false, - "description": "until: the property name, or a colon path through what the node holds, \"_game:run:day\", read the way runtime_inspect property reads one, a step written as a call included. Walked again on every frame, so a holder the game replaces while the wait is on is followed.", + "description": "until: the property name, or a colon path through what the node holds, \"_game:run:day\", read the way runtime_inspect property reads one, a step written as a call included, and one axis of a vector as well: \"global_position:x\" with compare \">=\" stops once a thing has travelled far enough. Walked again on every frame, so a holder the game replaces while the wait is on is followed.", "type": "string" }, "says": {
Changed 2026-10-14 11:37 UTC
5 descriptions rewritten, 5 schema or annotation changes with the description left byte-identical.
--- pinned/editor_launch +++ observed/editor_launch { "annotations": { "destructiveHint": true, - "description": "Opens the Godot editor on a project, or restarts or closes the one already connected. close saves open scenes, quits the editor and waits for its process to go, which is the only way to end a hidden one short of killing it; settingsDropped in its answer says what that save took out of project.godot. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back. Operations: open (needs projectPath): open the editor on a project; restart: restart the connected editor and wait for it; close: save and quit the connected editor, and wait for it to go. Default open.", + "description": "Opens the Godot editor on a project, or restarts or closes the one already connected. close saves open scenes, quits the editor and waits for its process to go, which is the only way to end a hidden one short of killing it; settingsDropped in its answer says what that save took out of project.godot. An editor this server opened that has not connected yet has nothing of this server's to save, so close stops its process instead and answers beforeConnecting. open answers once the process has started, and the editor dials in after its import; a call needing the editor made before then waits for the one this server opened, for as long as a restart waits, and is refused naming it if it has not arrived. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back. Operations: open (needs projectPath): open the editor on a project; restart: restart the connected editor and wait for it; close: save and quit the connected editor, and wait for it to go. Default open.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/editor_output +++ observed/editor_output { "annotations": { "openWorldHint": false, + "projectPath": { + "description": "The project whose output you mean to read. It picks nothing, since this reads the one run or editor this server holds, and a call naming another project than that one's is refused rather than answered with output from a different game.", + "type": "string" + }, "severity": { "description": "The least severe entry to include. Default info, which is everything.", "enum": [
--- pinned/editor_run +++ observed/editor_run { "annotations": { "destructiveHint": true, - "description": "The engine's own flags, before the separator, such as [\"--verbose\"] or [\"--fixed-fps\", \"60\"], a value going as the next entry. Only those that change what the engine logs, draws for debugging, checks on the GPU or how it times frames: --verbose, --quiet, --debug-collisions, --debug-paths, --debug-navigation, --debug-avoidance, --debug-stringnames, --debug-canvas-item-redraw, --debug-mute-audio, --print-fps, --gpu-profile, --gpu-validation, --gpu-abort, --disable-vsync, --frame-delay, --time-scale, --fixed-fps, --max-fps, --resolution. Any other is refused, since the engine's command line can also run scripts, open other projects and write files. A run with any is started by this server, as one with args is. A check given --verbose answers with everything printed rather than only errors and warnings, since what verbose adds, the instances leaked at exit among it, is printed as ordinary lines.", + "description": "The engine's own flags, before the separator, such as [\"--verbose\"] or [\"--fixed-fps\", \"60\"], a value going as the next entry. Only those that change what the engine logs, draws for debugging, checks on the GPU, which renderer draws, how it times frames or whether it builds the tree a screen reader reads, and Movie Maker's --write-movie: --verbose, --quiet, --debug-collisions, --debug-paths, --debug-navigation, --debug-avoidance, --debug-stringnames, --debug-canvas-item-redraw, --debug-mute-audio, --print-fps, --gpu-profile, --gpu-validation, --gpu-abort, --disable-vsync, --frame-delay, --time-scale, --fixed-fps, --max-fps, --resolution, --write-movie, --rendering-driver, --rendering-method, --accessibility. Any other is refused, since the engine's command line can also run scripts, open other projects and write files. --rendering-driver takes vulkan, d3d12, metal, opengl3, opengl3_es, opengl3_angle or dummy, and --rendering-method forward_plus, mobile or gl_compatibility, so the same scene can be measured on another driver and a project's OpenGL fallback exercised on a machine that has Vulkan; they choose what draws a run with a window. --accessibility always has the game build the tree a screen reader reads, as the project's accessibility/general/accessibility_support at Always Active does, so runtime_inspect accessibility can read it; otherwise the engine builds it only when a screen reader is running. --write-movie takes an absolute path outside the project, in a directory that exists, ending in .avi (one file, video and sound) or .png (one numbered PNG per frame and a .wav beside them), where nothing it would write is there already; it needs a start with headless: false, since the engine crashes writing a movie headless, and it records at the project's editor/movie_writer/fps unless --fixed-fps says otherwise. A run with any is started by this server, as one with args is. A check given --verbose answers with everything printed rather than only errors and warnings, since what verbose adds, the instances leaked at exit among it, is printed as ordinary lines.", "items": { "type": "string" }, - "description": "Absolute path to the project directory, the one holding project.godot. stop, wait: optional, the project whose run is meant; a run of another project is refused rather than touched.", + "description": "Absolute path to the project directory, the one holding project.godot. stop, wait, profile: optional, the project whose run is meant; a run of another project is refused rather than touched.", "type": "string" }, "runtimeWaitMs": {
--- pinned/project_import +++ observed/project_import { "annotations": { "destructiveHint": true, - "description": "set_options: import options keyed as the .import file spells them, {\"compress/mode\": 1}.", + "description": "set_options: import options keyed as the .import file spells them, {\"compress/mode\": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {\"opentype_features\": {\"liga\": 0}} turns ligatures off. Both are written as the numbers the engine reads.", "type": "object" }, "projectPath": {
--- pinned/project_settings +++ observed/project_settings { "annotations": { "destructiveHint": true, - "description": "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot. Operations: get: read one setting, or every setting under prefix; set (needs setting, value): write one setting; add_autoload (needs name, path): register an autoload singleton; remove_autoload (needs name): unregister an autoload; set_main_scene (needs scenePath): choose the scene the game starts in; add_input_action (needs actionName, events): register an input action and its events; remove_input_action (needs actionName): remove an input action the project defines; enable_plugin (needs pluginName): enable an addon; disable_plugin (needs pluginName): disable an addon; add_audio_bus (needs busName): add an audio bus; remove_audio_bus (needs busName): remove an audio bus, sending what sent to it on to where it sent; set_audio_bus_effect (needs busIndex, effectIndex, effectType): put an effect in a slot on a bus; set_audio_bus_volume (needs busIndex, volumeDb): set a bus volume.", + "description": "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. A set of the value the setting already holds, in the same type, writes nothing and answers unchanged true and saved false. Every write keeps the sections and keys of project.godot in the order the file had them, so a change touches only its own lines. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot. Each setting under prefix carries at_default too, so a family read separates what the project sets from the engine's defaults; it is absent for a setting the engine registers no default for, such as an input action the project added, which only the project can have set. Operations: get: read one setting, or every setting under prefix; set (needs setting, value): write one setting; add_autoload (needs name, path): register an autoload singleton; remove_autoload (needs name): unregister an autoload; set_main_scene (needs scenePath): choose the scene the game starts in; add_input_action (needs actionName, events): register an input action and its events; remove_input_action (needs actionName): remove an input action the project defines; enable_plugin (needs pluginName): enable an addon; disable_plugin (needs pluginName): disable an addon; add_audio_bus (needs busName): add an audio bus; remove_audio_bus (needs busName): remove an audio bus, sending what sent to it on to where it sent; set_audio_bus_effect (needs busIndex, effectIndex, effectType): put an effect in a slot on a bus; set_audio_bus_volume (needs busIndex, volumeDb): set a bus volume.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/project_test +++ observed/project_test { "annotations": { "destructiveHint": false, + "engineArgs": { + "description": "The engine's own flags for the run, such as [\"--verbose\"], a value going as the next entry. Only those that change what the engine logs, draws for debugging, checks on the GPU, which renderer draws, how it times frames or whether it builds the tree a screen reader reads: --verbose, --quiet, --debug-collisions, --debug-paths, --debug-navigation, --debug-avoidance, --debug-stringnames, --debug-canvas-item-redraw, --debug-mute-audio, --print-fps, --gpu-profile, --gpu-validation, --gpu-abort, --disable-vsync, --frame-delay, --time-scale, --fixed-fps, --max-fps, --resolution, --write-movie, --rendering-driver, --rendering-method, --accessibility. Any other is refused. With --verbose, engineEntries holds the ordinary lines the run printed as well, which is where the engine names the instances leaked and the resources still in use at exit.", + "items": { + "type": "string" + }, + "type": "array" + }, "failFast": { "description": "Stop each suite at its first failing case. Default false: every case runs. With it on, the counts are of what ran, notRun says how many cases were left, and fixing what is named and running again finds the next one, which looks like a flaky tier and is not.", "type": "boolean" - "description": "Suites or cases to leave out, as \"suite_name\" or \"suite_name:test_name\".", + "description": "Suites or cases to leave out, as \"suite_name\" or \"suite_name:test_name\". A bare test name is read as a suite name and leaves out nothing; an entry that named a case which ran all the same comes back under ignoreNotApplied with the form that would have left it out.", "items": { "type": "string" },
--- pinned/runtime_input +++ observed/runtime_input { "annotations": { "destructiveHint": false, - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release; joypad_button (needs button): press a controller button, or hold it, as an InputEventJoypadButton, so a game reading the pad in _input or _unhandled_input gets what a controller sends; an action bound to the button fires with it, as it would for a real one. Pressed and released a frame apart unless pressed says otherwise; joypad_motion (needs axis, value): move a stick or trigger axis to a value, as an InputEventJoypadMotion, which is what a game reading the sticks gets; the axis stays there until another motion moves it. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": { + "axis": { + "description": "joypad_motion: the stick or trigger axis, by name (left_x, left_y, right_x, right_y, trigger_left, trigger_right) or by its JoyAxis number.", + "type": [ + "string", + "number" + ] + }, "button": { - "description": "click, mouse_click: left, right, middle, wheel_up or wheel_down, or a button number. Default left.", + "description": "click, mouse_click: left, right, middle, wheel_up or wheel_down, or a button number. Default left. joypad_button: the controller button, by name (a, b, x, y, back, guide, start, left_stick, right_stick, left_shoulder, right_shoulder, dpad_up, dpad_down, dpad_left, dpad_right, misc1, paddle1 to paddle4, touchpad) or by its JoyButton number; a, b, x and y are the bottom, right, left and top face buttons, as Godot names them, whatever the controller prints on them.", "type": [ "string", "number" + "device": { + "description": "joypad_button, joypad_motion: which controller, counting from 0. Default 0.", + "minimum": 0, + "type": "number" + }, "doubleClick": { "description": "click, mouse_click: default false.", "type": "boolean" - "mouse_motion" + "mouse_motion", + "joypad_button", + "joypad_motion" ], "type": "string" }, - "description": "action, key, mouse_click: leave it out and the press is a whole one, down and up a frame apart. true holds it down, false lets go of one being held. A wheel button held down is still a whole step, since a wheel is never held.", + "description": "action, key, mouse_click, joypad_button: leave it out and the press is a whole one, down and up a frame apart. true holds it down, false lets go of one being held. A wheel button held down is still a whole step, since a wheel is never held.", "type": "boolean" }, "projectPath": { + "value": { + "description": "joypad_motion: where the axis is, -1 to 1: down and right are positive on a stick, and a trigger runs 0 to 1. The axis stays there until another motion moves it, as a stick held over does; send 0 to let it go back.", + "maximum": 1, + "minimum": -1, + "type": "number" + }, "x": { "description": "mouse_click, mouse_motion: window pixels.", "type": "number"
--- pinned/runtime_inspect +++ observed/runtime_inspect { "annotations": { "openWorldHint": false, - "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, or the performance metrics. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. Default tree.", + "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, the performance metrics, or what it tells a screen reader. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count; accessibility: what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role as UI Automation names it (text for static text, button, check_box, group for a control holding others), its name, its value where it has one, its bounds in the window's pixels, the actions a screen reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one holding the focus. The name is what is read aloud, so an empty one is read as nothing; static text takes it from DisplayServer.accessibility_update_set_value, not from a name set on it, and the answer says under _note how many text elements on screen have none. With no control focused, the engine reports the first node under the root as focused, which in a run with the runtime is GdharnessRuntime. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or with the project's accessibility/general/accessibility_support at Always Active, since the engine builds the tree only when asked; reading it takes a second or two, starting PowerShell inside the game so it reaches the game's own desktop. Default tree.", "inputSchema": { "additionalProperties": false, "properties": { - "metrics" + "metrics", + "accessibility" ], "type": "string" }, - "description": "How long to wait for the answer before answering pending with a requestId, which runtime_invoke op result collects the reply by. Default 10000, or GDHARNESS_RUNTIME_TIMEOUT_MS.", + "description": "How long to wait for the answer before answering pending with a requestId, which runtime_invoke op result collects the reply by. Default 10000, or GDHARNESS_RUNTIME_TIMEOUT_MS; accessibility defaults to 30000 or that, whichever is longer.", "minimum": 1, "type": "integer" }
--- pinned/runtime_invoke +++ observed/runtime_invoke { "annotations": { "destructiveHint": true, - "description": "call: the arguments, fitted to the method's parameter types. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused.", + "description": "call: the arguments, fitted to the method's parameter types. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused.", "type": "array" }, "method": { - "description": "set: the value, fitted to the property's type. \"\" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name." + "description": "set: the value, fitted to the property's type. A vector, colour or the like may be written as its fields, {\"x\": 140, \"y\": 300}, or tagged, {\"_type\": \"Vector2\", \"x\": 140, \"y\": 300}, which is the form a read answers with. \"\" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name." } }, "required": [
--- pinned/scene_tree +++ observed/scene_tree { "annotations": { "openWorldHint": false, - "description": "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected.", + "description": "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected. For the nodes of a game that is running, runtime_inspect tree, which needs no editor.", "inputSchema": { "additionalProperties": false, "properties": {
Changed 2026-10-13 13:59 UTC
11 descriptions rewritten, 19 schema or annotation changes with the description left byte-identical.
--- pinned/debug_breakpoint +++ observed/debug_breakpoint { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Sets or removes a breakpoint through the editor's debug adapter. Needs the editor, not a running game: set them first, then editor_run, and the game stops where you asked. The editor keeps a breakpoint set this way for one play, so every breakpoint this server holds is sent again before each play editor_run starts, and the start answer lists them under breakpoints. The set is also kept in the project, for the server that comes after a reconnect: a breakpoint stays until it is removed here, whichever server set it. Every answer lists the whole set under held, and the breakpoints the user set in the editor's own gutter under setInEditor; setting one here never takes those away, and removing a line here clears it whoever set it. Godot clears every breakpoint in the editor when a debug session opens unless network/debug_adapter/sync_breakpoints is on, which the addon turns on as it loads; an editor running an older addon is said so under breakpointsAtRisk, here and in editor_status. Operations: set: set a breakpoint; remove: remove a breakpoint.", "inputSchema": { "additionalProperties": false, - "name": "debug_breakpoint" + "name": "debug_breakpoint", + "title": "Breakpoints" }
--- pinned/debug_control +++ observed/debug_control { + "annotations": { + "destructiveHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Continues or steps the debugged game through the editor's debug adapter. A step waits for the game to stop again and answers with the stack where it ended up; one that has not stopped the game again within ten seconds answers heldAgain false and no stack. There is no pause and no step_out: Godot's adapter answers a pause by reporting the game stopped and leaving it running, and implements no stepOut at all, so hold the game where you want it with a breakpoint and step over or into from there. Operations: continue: resume after a breakpoint; step_over: run the current line; step_into: run the current line, stopping inside whatever it calls.", "inputSchema": { "additionalProperties": false, - "name": "debug_control" + "name": "debug_control", + "title": "Step the debugger" }
--- pinned/debug_state +++ observed/debug_state { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the debug adapter's console output so far, its last thousand lines, with any earlier ones counted under notShown. A stop is reported by the editor's adapter only to the sessions connected when it happened, so a server that connected afterwards, which is every server after a reconnect, is shown a thread and no frames about a game still sitting at its breakpoint. That server asks the runtime instead: a held game accepts a connection and never answers, and the refusal then says held rather than running, and that the stack cannot be read from here. debug_control continue lets it go, and the next stop is one this session is told about, with its stack. Operations: stack: the stack trace; variables: locals, members and globals at a frame, with their values; output: console output captured through the debug adapter. Default stack.", "inputSchema": { "additionalProperties": false, - "name": "debug_state" + "name": "debug_state", + "title": "Debugger state" }
--- pinned/editor_classes +++ observed/editor_classes { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "The engine's ClassDB: find classes, read one in full, or walk an inheritance tree. Operations: query: classes matching a filter or category; info (needs className): methods, properties, signals and enums of one class, or one member of it by name; inheritance (needs className): ancestors and descendants of one class. Default query.", "inputSchema": { "additionalProperties": false, - "name": "editor_classes" + "name": "editor_classes", + "title": "Engine classes" }
--- pinned/editor_launch +++ observed/editor_launch { - "description": "Opens the Godot editor on a project, or restarts the one already connected. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back. Operations: open (needs projectPath): open the editor on a project; restart: restart the connected editor and wait for it. Default open.", + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Opens the Godot editor on a project, or restarts or closes the one already connected. close saves open scenes, quits the editor and waits for its process to go, which is the only way to end a hidden one short of killing it; settingsDropped in its answer says what that save took out of project.godot. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back. Operations: open (needs projectPath): open the editor on a project; restart: restart the connected editor and wait for it; close: save and quit the connected editor, and wait for it to go. Default open.", "inputSchema": { "additionalProperties": false, "properties": { - "restart" + "restart", + "close" ], "type": "string" }, - "name": "editor_launch" + "name": "editor_launch", + "title": "Open, restart or close the editor" }
--- pinned/editor_output +++ observed/editor_output { - "description": "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. startedAt is when the run began and elapsedMs how long it has been going; for a run that is over, endedAt is when it ended and elapsedMs how long it ran, and both are left out when nobody saw the end, as for a game closed in the editor or a run picked up from a note written before ends were recorded. Operations: run: What the game a run is playing has printed, which is everything described above; editor: What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it. Default run.", + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries: clean is true when no error was reported, and warnings, counted beside it, do not make it false. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. startedAt is when the run began and elapsedMs how long it has been going; for a run that is over, endedAt is when it ended and elapsedMs how long it ran, and both are left out when nobody saw the end, as for a game closed in the editor or a run picked up from a note written before ends were recorded. Operations: run: What the game a run is playing has printed, which is everything described above; editor: What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it. The engine's progress bars are one line per task, saying how far an unfinished one got. Default run.", "inputSchema": { "additionalProperties": false, "properties": { - "name": "editor_output" + "name": "editor_output", + "title": "Game and editor output" }
--- pinned/editor_rescan +++ observed/editor_rescan { + "annotations": { + "destructiveHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it, and the answer says how long under waitedForEditorScanMs. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. The walk also passes over a file whose .uid alone changed, so the editor keeps the old UID and every engine reading its cache warns of a duplicate; before scanning, each file whose .uid names a UID the editor does not hold for it is read again and named under uidsReread, and files on disk naming one UID between them are named under uidsDuplicatedOnDisk, which reading again cannot settle. A class the scan brings in is named under broughtIn, and every script naming it is reloaded in the editor and named under dependentsReloaded: what the editor compiled from them while it could not see the class stays compiled that way after the scan, and a script extending a new base class went on reporting \"Could not find base class\" until reloaded. One that would not reload is named under dependentsNotReloaded and the call is not ok, as it is not when the script named in reloadScript was not reloaded. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.", "inputSchema": { "additionalProperties": false, - "name": "editor_rescan" + "name": "editor_rescan", + "title": "Rescan the project" }
--- pinned/editor_run +++ observed/editor_run { - "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, sorted by self time, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A run started with profile: true is profiled from its start. A game the editor plays that was not is profiled from the moment this is asked, and the answer says so; a run this server started without it is refused, since its debugger is fixed as it starts. Default start.", + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run, naming the process ended under endedPid, and answer with the errors and warnings it printed under entries (editor_output has the rest); a game that started processes of its own keeps them unless andChildren says to end those too; check (needs projectPath): boot headless, quit after a few frames, and report the verdict: booted is true only for a run that quit on its own within timeoutMs, exited 0 and printed no error, so a game that reached _ready and then raised an error answers false; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended, sorted by self time. A run started with profile: true is profiled from its start; a game the editor plays that was not is profiled from the moment this is asked; a run this server started without it is refused, since its debugger is fixed as it starts. Default start. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "The game's own arguments, what OS.get_cmdline_user_args() answers, such as [\"--level=2\"]. The separator is added here. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so the debug_* tools do not answer for it.", + "description": "The game's own arguments, what OS.get_cmdline_user_args() answers, such as [\"--level=2\"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so the debug_* tools do not answer for it.", "items": { "type": "string" }, + "engineArgs": { + "description": "The engine's own flags, before the separator, such as [\"--verbose\"] or [\"--fixed-fps\", \"60\"], a value going as the next entry. Only those that change what the engine logs, draws for debugging, checks on the GPU or how it times frames: --verbose, --quiet, --debug-collisions, --debug-paths, --debug-navigation, --debug-avoidance, --debug-stringnames, --debug-canvas-item-redraw, --debug-mute-audio, --print-fps, --gpu-profile, --gpu-validation, --gpu-abort, --disable-vsync, --frame-delay, --time-scale, --fixed-fps, --max-fps, --resolution. Any other is refused, since the engine's command line can also run scripts, open other projects and write files. A run with any is started by this server, as one with args is. A check given --verbose answers with everything printed rather than only errors and warnings, since what verbose adds, the instances leaked at exit among it, is printed as ordinary lines.", + "items": { + "type": "string" + }, + "type": "array" + }, "env": { "description": "Environment variables for this run alone, as a map of strings, over the server's own environment. Like savesIn, a run carrying any is started by this server rather than played by the editor. Names beginning with GDHARNESS_ are refused: they are this server's contract with the addon in the game, and the runtime directory is set here whatever the rest of the environment says, so a game that moves TEMP still announces where this server looks.", "type": "object" - "name": "editor_run" + "name": "editor_run", + "title": "Run the game" }
--- pinned/editor_status +++ observed/editor_status { - "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. startedAt says when it began, and endedAt when it ended, wherever something saw the end: a run adopted from a note another server wrote can have ended hours before this server started. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and which games with the runtime addon are reachable, of this project and of every other on the machine. Read connected with mayYetConnect: the editor dials this server and backs off between tries, so for the first half-minute after a server starts false means not reached yet as often as no editor; mayYetConnect true is worth waiting out, false is final, and restartInterrupted, restartUnderway, closedHere and editorsNotYetConnected say why. An editor that has connected and not yet greeted reports nothing about itself, and every other call made in that window waits up to ten seconds for it to settle. playingInEditor null means unanswered rather than nothing playing: processActive and runtimes do not depend on the editor answering. game.run names the run editor_output and editor_run stop answer for, and stays named after it ends. addonIsStale, on the editor and on each game, says it runs addon code other than what this server ships, and staleNote says which half moves: gdharness upgrade in the project, a restart, or a reconnect of this server. A false editor.lspListening or editor.dapListening comes with serversNote, and editor_launch restart binds the port again. editor.scan says whether the editor is scanning, which editor_run waits out before starting a game. A game announced in a protocol this server does not speak is listed under unreadable, during an upgrade that moves the protocol, until the halves match. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": {}, - "name": "editor_status" + "name": "editor_status", + "title": "Editor status" }
--- pinned/project_dependencies +++ observed/project_dependencies { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "What a resource depends on, or what depends on it.", "inputSchema": { "additionalProperties": false, - "name": "project_dependencies" + "name": "project_dependencies", + "title": "Project dependencies" }
--- pinned/project_export +++ observed/project_export { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Export presets and exports. Operations: list: the presets in export_presets.cfg; run (needs preset, outputPath): export with a preset.", "inputSchema": { "additionalProperties": false, - "name": "project_export" + "name": "project_export", + "title": "Export" }
--- pinned/project_import +++ observed/project_import { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "The import pipeline: what needs importing, how a resource is imported, reimports, UIDs, and the global class list the editor and the engine read. Operations: status: which resources are outdated or failed, or one resource with resourcePath, each with its reason. Judged by content with the checks the editor makes before it reimports: a sidecar changed since its import, as setting an option without a reimport leaves it; a sidecar with no uid or written for another file; an output under .godot/imported that is gone (missing_outputs) or changed; a source whose hash differs from the one its import recorded, or with no record. Beyond the editor, a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without; a scene whose import runs an import script is not judged on images). failed is an import that failed, which the editor never tries again on its own. Without resourcePath, a sidecar whose source is gone is listed as missing_source; options (needs resourcePath): the import options of one resource; set_options (needs resourcePath, options): change import options in the sidecar, then apply them with a reimport of that resource; reimport: reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs; uid (needs resourcePath): the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header; refresh_uids: import the project so every script and shader has its .uid sidecar, and name under uidsCreated the ones this made and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched; refresh_classes: rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so \"added: []\" means the file was already right rather than that nothing is wrong. A class the cache held after the last rebuild and had lost when this one began is named under lostSinceLastRebuild: the editor wrote the file without it in between, on a save or a scan, and will again until restarted.", "inputSchema": { "additionalProperties": false, - "name": "project_import" + "name": "project_import", + "title": "Asset imports" }
--- pinned/project_info +++ observed/project_info { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, with optional sections on top.", "inputSchema": { "additionalProperties": false, - "name": "project_info" + "name": "project_info", + "title": "Project info" }
--- pinned/project_search +++ observed/project_search { - "description": "Searches text or a regular expression across project files and returns file paths with line numbers. What the engine steps over is not searched: directories spelled with a dot, node_modules, and any directory holding a .gdignore. A vendored engine or an export directory is therefore absent from the results rather than matching the code you are looking for in a copy nobody runs.", + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "Searches text or a regular expression across project files and returns file paths with line numbers. What the engine steps over is not searched: directories spelled with a dot, node_modules, and any directory holding a .gdignore. A vendored engine or an export directory is therefore absent from the results rather than matching the code you are looking for in a copy nobody runs. The addons gdharness installed are not searched either, since nobody on the project wrote them.", "inputSchema": { "additionalProperties": false, "properties": { - "name": "project_search" + "name": "project_search", + "title": "Search the project" }
--- pinned/project_settings +++ observed/project_settings { - "description": "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. Operations: get: read one setting, or every setting under prefix; set (needs setting, value): write one setting; add_autoload (needs name, path): register an autoload singleton; remove_autoload (needs name): unregister an autoload; set_main_scene (needs scenePath): choose the scene the game starts in; add_input_action (needs actionName, events): register an input action and its events; enable_plugin (needs pluginName): enable an addon; disable_plugin (needs pluginName): disable an addon; add_audio_bus (needs busName): add an audio bus; set_audio_bus_effect (needs busIndex, effectIndex, effectType): put an effect in a slot on a bus; set_audio_bus_volume (needs busIndex, volumeDb): set a bus volume.", + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot. Operations: get: read one setting, or every setting under prefix; set (needs setting, value): write one setting; add_autoload (needs name, path): register an autoload singleton; remove_autoload (needs name): unregister an autoload; set_main_scene (needs scenePath): choose the scene the game starts in; add_input_action (needs actionName, events): register an input action and its events; remove_input_action (needs actionName): remove an input action the project defines; enable_plugin (needs pluginName): enable an addon; disable_plugin (needs pluginName): disable an addon; add_audio_bus (needs busName): add an audio bus; remove_audio_bus (needs busName): remove an audio bus, sending what sent to it on to where it sent; set_audio_bus_effect (needs busIndex, effectIndex, effectType): put an effect in a slot on a bus; set_audio_bus_volume (needs busIndex, volumeDb): set a bus volume.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "Autoloads: Godot 4 loads every autoload in the list, so false is refused. Use global: false to keep the name out of the global scope, or remove_autoload to stop it loading.", + "description": "add_autoload: Godot 4 loads every autoload in the list, so false is refused. Use global: false to keep the name out of the global scope, or remove_autoload to stop it loading. set_audio_bus_effect: whether the effect is on. Default true.", "type": "boolean" }, "events": { + "remove_input_action", "enable_plugin", "disable_plugin", "add_audio_bus", + "remove_audio_bus", "set_audio_bus_effect", "set_audio_bus_volume" ], - "name": "project_settings" + "name": "project_settings", + "title": "Project settings" }
--- pinned/project_test +++ observed/project_test { + "annotations": { + "destructiveHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, every report under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", "inputSchema": { "additionalProperties": false, - "name": "project_test" + "name": "project_test", + "title": "Run tests" }
--- pinned/resource_edit +++ observed/resource_edit { - "description": "Resource files: create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. Needs the editor connected. Operations: create (needs resourceType): a new resource of any class; modify (needs properties): set properties on an existing resource; create_shader (needs shaderType): a .gdshader file; create_tileset (needs sources): a TileSet from texture atlases; set_theme_color (needs controlType, colorName, color): a colour in a Theme; set_theme_font_size (needs controlType, fontSizeName, size): a font size in a Theme.", + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as \"Button/colors/font_color\". Needs the editor connected. Operations: read: the properties a resource file sets; create (needs resourceType): a new resource of any class; modify (needs properties): set properties on an existing resource; create_shader (needs shaderType): a .gdshader file; create_tileset (needs sources): a TileSet from texture atlases; set_theme_color (needs controlType, colorName, color): a colour in a Theme; set_theme_font_size (needs controlType, fontSizeName, size): a font size in a Theme.", "inputSchema": { "additionalProperties": false, "properties": { + "includeDefaults": { + "description": "read: include properties still at their default. Default false.", + "type": "boolean" + }, "op": { "description": "What to do.", "enum": [ + "read", "create", "modify", "create_shader", - "name": "resource_edit" + "name": "resource_edit", + "title": "Edit resources" }
--- pinned/runtime_capture +++ observed/runtime_capture { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "A picture of the running game: the whole screen, one region or node of it at full resolution, or one viewport, as an image. The whole screen includes the game's own dialogs and popups where they sit, also when a project opens them as windows of their own (subwindows not embedded). Needs the game running with a window. A capture asked for before the game's first frame is drawn waits for that frame, since the game announces itself before it and the viewport holds nothing it drew until then. Operations: screenshot: the screen; viewport: one viewport's texture. Default screenshot.", "inputSchema": { "additionalProperties": false, - "name": "runtime_capture" + "name": "runtime_capture", + "title": "Capture the screen" }
--- pinned/runtime_input +++ observed/runtime_input { - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says \"the words\", or \"the middle of the label\" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", + "annotations": { + "destructiveHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, named by nodePath or by says with the words on it (found under nodePath when both are given), or on a 3D node where it is drawn. A control out of sight inside a ScrollContainer is scrolled to first, and a link in a RichTextLabel is pressed where its words are drawn. A disabled button, a control that does not process and one whose pointer mouse_behavior_recursive turned off are refused, since the press would do nothing; by says, one the pointer can reach is picked over it; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list: the menu is opened, the item found in it as it stands then, and pressed through the engine's own path, which needs no window and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release. The tool reference in the gdharness skill, and the site, say what every field of the answer means.", "inputSchema": { "additionalProperties": false, "properties": { - "name": "runtime_input" + "name": "runtime_input", + "title": "Send input to the game" }
--- pinned/runtime_inspect +++ observed/runtime_inspect { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, or the performance metrics. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. Default tree.", "inputSchema": { "additionalProperties": false, - "name": "runtime_inspect" + "name": "runtime_inspect", + "title": "Inspect the running game" }
--- pinned/runtime_invoke +++ observed/runtime_invoke { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Sets a property or calls a method on a node in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors and warnings the engine reported while a called method ran come back under errors, the first 20, with errors_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes. Operations: set (needs nodePath, property, value): set a property, on a node or on an object it holds; call (needs nodePath, method): call a method, on a node or on an object it holds, and return its result; result (needs requestId): collect the reply to a set or call whose wait ran out, by the requestId it answered with.", "inputSchema": { "additionalProperties": false, - "name": "runtime_invoke" + "name": "runtime_invoke", + "title": "Set or call in the running game" }
--- pinned/runtime_wait +++ observed/runtime_wait { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal (needs nodePath, signal): wait for a signal and answer with what it carried; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found.", "inputSchema": { "additionalProperties": false, - "name": "runtime_wait" + "name": "runtime_wait", + "title": "Wait in the running game" }
--- pinned/scene_animation +++ observed/scene_animation { - "description": "Animations in an AnimationPlayer and states in an AnimationTree state machine. Needs the editor connected. Operations: create (needs playerNodePath, animationName): a new animation in an AnimationPlayer; add_track (needs playerNodePath, animationName, track): a property or method track with keyframes; add_state (needs animTreePath, stateName, animationName): a state playing an animation, in an AnimationTree; connect_states (needs animTreePath, fromState, toState): a transition between two states.", + "annotations": { + "destructiveHint": false, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. Needs the editor connected. Operations: read (needs playerNodePath): the animations of an AnimationPlayer, or one with its tracks; read_states (needs animTreePath): the states and transitions of an AnimationTree state machine; create (needs playerNodePath, animationName): a new animation in an AnimationPlayer; add_track (needs playerNodePath, animationName, track): a property or method track with keyframes; add_state (needs animTreePath, stateName, animationName): a state playing an animation, in an AnimationTree; connect_states (needs animTreePath, fromState, toState): a transition between two states.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "The animation in the player. add_state: the one that state plays.", + "description": "The animation in the player. read: the one to answer with whole; without it, read lists them. add_state: the one that state plays.", "type": "string" }, "fromState": { + "read", + "read_states", "create", "add_track", "add_state", - "description": "add_state: a nested state machine. Default the root.", + "description": "A nested state machine, by the names of the states that lead to it. Default the root.", "type": "string" }, "stateName": { - "name": "scene_animation" + "name": "scene_animation", + "title": "Animations" }
--- pinned/scene_create +++ observed/scene_create { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Creates a scene file where none is, saves one, or saves a copy under a new path. A save of a scene open in the editor saves the editor's copy, unsaved changes included. Needs the editor connected. Operations: create: a new scene with one root node; save: save the scene as it is in the editor; save_as (needs newPath): save a copy under newPath. Default create.", "inputSchema": { "additionalProperties": false, + "rootNodeName": { + "description": "create: the root node name. Default the class name, as the editor names a new root.", + "type": "string" + }, "rootNodeType": { "description": "create: the root node class. Default Node.", "type": "string" + }, + "script": { + "description": "create: a script to attach to the root.", + "type": "string" } }, "required": [ - "name": "scene_create" + "name": "scene_create", + "title": "Create or save a scene" }
--- pinned/scene_node +++ observed/scene_node { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. Needs the editor connected. Operations: add (needs nodeType, nodeName): add a node of any class; get (needs nodePath): read a node's properties; set (needs nodePath, properties): set properties on a node; duplicate (needs nodePath, newName): copy a node and its children; reparent (needs nodePath, newParentPath): move a node under another parent; delete (needs nodePath): remove a node and its children; set_tilemap_cells (needs nodePath, cells): place tiles in a TileMapLayer or TileMap.", "inputSchema": { "additionalProperties": false, - "name": "scene_node" + "name": "scene_node", + "title": "Scene nodes" }
--- pinned/scene_signal +++ observed/scene_signal { - "description": "Signal connections in a scene file. Needs the editor connected. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, + "description": "Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. Needs the editor connected. Operations: connect (needs sourceNodePath, signalName, targetNodePath, methodName): connect a signal to a method; disconnect (needs sourceNodePath, signalName, targetNodePath, methodName): remove a connection; list: every connection in the scene.", "inputSchema": { "additionalProperties": false, "properties": { - "name": "scene_signal" + "name": "scene_signal", + "title": "Scene signals" }
--- pinned/scene_tree +++ observed/scene_tree { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected.", "inputSchema": { "additionalProperties": false, - "name": "scene_tree" + "name": "scene_tree", + "title": "Scene tree" }
--- pinned/script_diagnostics +++ observed/script_diagnostics { + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, "description": "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", "inputSchema": { "additionalProperties": false, - "name": "script_diagnostics" + "name": "script_diagnostics", + "title": "Script diagnostics" }
--- pinned/script_edit +++ observed/script_edit { + "annotations": { + "destructiveHint": true, + "openWorldHint": false, + "readOnlyHint": false + }, "description": "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, of which the newest ten are kept, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so. Operations: create: a new script file; modify (needs modifications): add to an existing script; rename (needs symbol, newName): rename a class or a member wherever the engine uses it, across the project.", "inputSchema": { "additionalProperties": false, - "name": "script_edit" + "name": "script_edit", + "title": "Edit scripts" }
--- pinned/script_info +++ observed/script_info { - "description": "What a script contains: its structure from the file, or symbols, completions and hover text from the editor's language server. Operations: structure: functions, variables, signals, class_name and extends, read from the file; symbols: document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration; completion (needs line, character): completions at a position; hover (needs line, character): hover text at a position. Default structure.", + "annotations": { + "openWorldHint": false, + "readOnlyHint": true + }, + "description": "What a script contains: its structure from the file, or symbols, completions and hover text from the editor's language server. Operations: structure: functions, variables, signals, class_name and extends, read from the file; symbols: document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration; completion (needs line, character): completions at a position, narrowed to the part of a name written before it, at most 100; hover (needs line, character): hover text at a position. Default structure.", "inputSchema": { "additionalProperties": false, "properties": { - "name": "script_info" + "name": "script_info", + "title": "Script info" }
Changed 2026-10-12 15:04 UTC
1 description rewritten.
--- pinned/runtime_input +++ observed/runtime_input { - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says \"the words\", or \"the middle of the label\" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says \"the words\", or \"the middle of the label\" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", "inputSchema": { "additionalProperties": false, "properties": {
Changed 2026-10-11 12:32 UTC
4 descriptions rewritten, 1 schema or annotation change with the description left byte-identical.
--- pinned/editor_output +++ observed/editor_output { - "description": "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. Operations: run: What the game a run is playing has printed, which is everything described above; editor: What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it. Default run.", + "description": "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. startedAt is when the run began and elapsedMs how long it has been going; for a run that is over, endedAt is when it ended and elapsedMs how long it ran, and both are left out when nobody saw the end, as for a game closed in the editor or a run picked up from a note written before ends were recorded. Operations: run: What the game a run is playing has printed, which is everything described above; editor: What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it. Default run.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/editor_run +++ observed/editor_run { - "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, sorted by self time, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. A run started with profile: true is profiled from its start. A game the editor plays that was not is profiled from the moment this is asked, and the answer says so; a run this server started without it is refused, since its debugger is fixed as it starts. Default start.", + "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, sorted by self time, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A run started with profile: true is profiled from its start. A game the editor plays that was not is profiled from the moment this is asked, and the answer says so; a run this server started without it is refused, since its debugger is fixed as it starts. Default start.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed.", + "description": "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed. A property's get or set block, which the profile names Class.@name_getter or Class.@name_setter, is asked about by that name or by the property's: the places are where the property is named, each saying instead of called whether it writes the property (access write), which runs the set block, or reads it (access read), which runs the get block, and the rows are both blocks'. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not places.", "type": "string" }, "env": { - "description": "start: how long to wait for the game to announce its runtime before answering. Default 5000, or half as long again as the last boot of this project took when that is more, up to 60000: the server notes how long each game took to announce, so a project that boots slowly is waited for from its second start on without being asked to. What it waits for is the first frame, so everything the game does before drawing one is inside it, including work the args just asked for: a flag that simulates six years of game time before anything is drawn makes the announcement that late, on a project that announces promptly without it. Raise it for such a run rather than reading runtime listening false as a fault. 0 does not wait at all, which is the one to pass for a scene that announces nothing by construction, such as a bench that prints and quits.", + "description": "start: how long to wait for the game to announce its runtime before answering. Default 5000, or half as long again as the slowest of the last five boots of the same scene when that is more, up to 60000: the server notes how long each game took to announce, per scene and for the main scene when none is named, since a boot moves with the load on the machine, so a scene that boots slowly is waited for from its second start on without being asked to, and a harness scene that announces late sets nothing for the main scene. What it waits for is the first frame, so everything the game does before drawing one is inside it, including work the args just asked for: a flag that simulates six years of game time before anything is drawn makes the announcement that late, on a project that announces promptly without it. Raise it for such a run rather than reading runtime listening false as a fault. 0 does not wait at all, which is the one to pass for a scene that announces nothing by construction, such as a bench that prints and quits.", "minimum": 0, "type": "integer" },
--- pinned/editor_status +++ observed/editor_status { - "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", + "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. startedAt says when it began, and endedAt when it ended, wherever something saw the end: a run adopted from a note another server wrote can have ended hours before this server started. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", "inputSchema": { "additionalProperties": false, "properties": {},
--- pinned/project_test +++ observed/project_test { - "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", + "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, every report under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/runtime_wait +++ observed/runtime_wait { "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal (needs nodePath, signal): wait for a signal and answer with what it carried; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found.", "inputSchema": { + "compare": { + "description": "until, with a property: how what the property reads stands to value before the wait is met. Default ==. != waits for it to read as anything else. >=, <=, > and < put a number in order, met on the first look past the value or level with it at the precision the property keeps, which is how a counter that steps rather than lands is waited on: a day counter on a fast clock went from 31 to 34 between two looks, and a wait for 32 ran out. For these the value is the number it is rather than fitted to the property, so a fraction on a whole-number property is a threshold rather than refused, and a property holding anything but a number is refused.", + "enum": [ + "==", + "!=", + ">=", + "<=", + ">", + "<" + ], + "type": "string" + }, "frames": { "description": "frames: how many to let pass, 1 to 600. More than that is refused.", "maximum": 600,
Changed 2026-10-10 12:18 UTC
3 descriptions rewritten.
--- pinned/editor_run +++ observed/editor_run { - "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, calls, and self and total milliseconds, sorted by self time, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a function the profiler started inside is left out, since its time would be counted from the engine's start. A run started with profile: true is profiled from its start. A game the editor plays that was not is profiled from the moment this is asked, and the answer says so; a run this server started without it is refused, since its debugger is fixed as it starts. Default start.", + "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, sorted by self time, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. A run started with profile: true is profiled from its start. A game the editor plays that was not is profiled from the moment this is asked, and the answer says so; a run this server started without it is refused, since its debugger is fixed as it starts. Default start.", "inputSchema": { "additionalProperties": false, "properties": { + "callersOf": { + "description": "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed.", + "type": "string" + }, "env": { "description": "Environment variables for this run alone, as a map of strings, over the server's own environment. Like savesIn, a run carrying any is started by this server rather than played by the editor. Names beginning with GDHARNESS_ are refused: they are this server's contract with the addon in the game, and the runtime directory is set here whatever the rest of the environment says, so a game that moves TEMP still announces where this server looks.", "type": "object" - "description": "start: profile the run's GDScript with the engine's own script profiler from its start, for editor_run profile to answer which functions took the time. A run this server starts dials the process that keeps it as its debugger, on loopback, with breakpoints and error breaks skipped so it never stops to wait; a game the editor plays is profiled through the editor's debugger session. Needs an engine built with debugging, which the editor binary is and a release export template is not. Default false.", + "description": "start: profile the run's GDScript with the engine's own script profiler from its start, for editor_run profile to answer which functions took the time. A run this server starts dials the process that keeps it as its debugger, on loopback, with breakpoints and error breaks skipped so it never stops to wait; a game the editor plays is profiled through the editor's debugger session. Needs an engine built with debugging, which the editor binary is and a release export template is not. The start answers profile.on and, under profile.totals, how the numbers will arrive. Default false.", "type": "boolean" }, "projectPath": {
--- pinned/editor_status +++ observed/editor_status { - "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it; null when there is none. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", + "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", "inputSchema": { "additionalProperties": false, "properties": {},
--- pinned/runtime_inspect +++ observed/runtime_inspect { - "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, or the performance metrics. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. Default tree.", + "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, or the performance metrics. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. Default tree.", "inputSchema": { "additionalProperties": false, "properties": {
Changed 2026-10-09 13:00 UTC
4 descriptions rewritten, 3 schema or annotation changes with the description left byte-identical.
--- pinned/editor_output +++ observed/editor_output { "description": "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. Operations: run: What the game a run is playing has printed, which is everything described above; editor: What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it. Default run.", "inputSchema": { - "description": "The most entries to answer with, newest kept. Default 200, and omitted says how many matching entries that left out. A run long enough to pass it needs this raised, not just filtered: filtering narrows what counts as matching, and the newest of those is still all one answer carries.", + "description": "The most entries to answer with, newest kept. Identical entries come back as one, at the first of them, with times saying how many were printed, so the limit counts distinct entries. Default 200, and omitted says how many distinct matching entries that left out. A run long enough to pass it needs this raised, not just filtered: filtering narrows what counts as matching, and the newest of those is still all one answer carries.", "minimum": 1, "type": "integer" },
--- pinned/editor_rescan +++ observed/editor_rescan { "description": "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it, and the answer says how long under waitedForEditorScanMs. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. The walk also passes over a file whose .uid alone changed, so the editor keeps the old UID and every engine reading its cache warns of a duplicate; before scanning, each file whose .uid names a UID the editor does not hold for it is read again and named under uidsReread, and files on disk naming one UID between them are named under uidsDuplicatedOnDisk, which reading again cannot settle. A class the scan brings in is named under broughtIn, and every script naming it is reloaded in the editor and named under dependentsReloaded: what the editor compiled from them while it could not see the class stays compiled that way after the scan, and a script extending a new base class went on reporting \"Could not find base class\" until reloaded. One that would not reload is named under dependentsNotReloaded and the call is not ok, as it is not when the script named in reloadScript was not reloaded. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.", "inputSchema": { + "listMembers": { + "description": "With reloadScript: also answer the whole member lists, heldBeforeReload and reloadedMethods for the methods, heldConstantsBeforeReload and reloadedConstants for the constants. Default false: a script holding hundreds of constants answers hundreds of lines twice to say what one reload added.", + "type": "boolean" + }, "projectPath": { "description": "Absolute path to the project directory, the one holding project.godot.", "type": "string" - "description": "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. The methods it has afterwards come back under reloadedMethods, because a reload that compiled nothing and answered OK is the failure worth catching, and what it held before under heldBeforeReload. Its constants come back the same way, under heldConstantsBeforeReload and reloadedConstants, with each enum value as Kind.SHORT: an enum member is held as a constant and never shows among the methods. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before. A @tool script is not reloaded either, since it runs inside the editor and reloading one from inside a call can take the editor down: reloadProblem says so, and editor_launch restart recompiles it. A path spelled in another case than the file is reloaded under the spelling on disk, which is the one the editor holds its copy under, and that spelling comes back under reloadedAs.", + "description": "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. What the reload changed comes back under methodsAdded, methodsRemoved, constantsAdded and constantsRemoved, with each enum value as Kind.SHORT among the constants, since an enum member is held as a constant and never shows among the methods; methodsHeld and constantsHeld count what the copy holds afterwards, because a reload that compiled nothing and answered OK is the failure worth catching. listMembers adds the whole lists. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before. A @tool script is not reloaded either, since it runs inside the editor and reloading one from inside a call can take the editor down: reloadProblem says so, and editor_launch restart recompiles it. A path spelled in another case than the file is reloaded under the spelling on disk, which is the one the editor holds its copy under, and that spelling comes back under reloadedAs.", "type": "string" }, "timeoutMs": {
--- pinned/editor_run +++ observed/editor_run { - "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does. Default start.", + "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does; profile: where the run spends its time by GDScript function, while it runs or after it ended: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, calls, and self and total milliseconds, sorted by self time, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a function the profiler started inside is left out, since its time would be counted from the engine's start. A run started with profile: true is profiled from its start. A game the editor plays that was not is profiled from the moment this is asked, and the answer says so; a run this server started without it is refused, since its debugger is fixed as it starts. Default start.", "inputSchema": { "additionalProperties": false, "properties": { + "limit": { + "description": "profile: how many functions to answer with, those with the most self time. Default 30.", + "minimum": 1, + "type": "integer" + }, "op": { "description": "What to do.", "enum": [ - "wait" + "wait", + "profile" ], "type": "string" }, + "profile": { + "description": "start: profile the run's GDScript with the engine's own script profiler from its start, for editor_run profile to answer which functions took the time. A run this server starts dials the process that keeps it as its debugger, on loopback, with breakpoints and error breaks skipped so it never stops to wait; a game the editor plays is profiled through the editor's debugger session. Needs an engine built with debugging, which the editor binary is and a release export template is not. Default false.", + "type": "boolean" + }, "projectPath": { "description": "Absolute path to the project directory, the one holding project.godot. stop, wait: optional, the project whose run is meant; a run of another project is refused rather than touched.", "type": "string"
--- pinned/project_test +++ observed/project_test { - "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. Needs gdUnit4 under addons/gdUnit4.", + "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/runtime_wait +++ observed/runtime_wait { "description": "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, a property reaching a value, or words appearing on a screen. Needs the game running with the runtime addon. Operations: frames (needs frames): let frames pass; signal (needs nodePath, signal): wait for a signal and answer with what it carried; until: wait for a property to read as a value, or for words to appear under a node, and answer with what it found.", "inputSchema": { - "description": "until: wait for these words to appear anywhere under nodePath, or anywhere in the game when no nodePath is given, instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Matched the way runtime_inspect find matches says: case-insensitive, a plain word anywhere in the text, a pattern with * or ? a glob over the whole of it, and alternatives separated by | met by whichever appears first (\"*won*|*lost*\"); on a node the player can see unless includeHidden says otherwise; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property. It looks every frame while a look is cheap and less often on a screen large enough for a look to cost a real share of a frame, so the game keeps its speed; the answer counts the frames the wait spanned and the looks it took.", + "description": "until: wait for these words to appear anywhere under nodePath, or anywhere in the game when no nodePath is given, instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Matched the way runtime_inspect find matches says: case-insensitive, a plain word anywhere in the text, a pattern with * or ? a glob over the whole of it, and alternatives separated by | met by whichever appears first (\"*won*|*lost*\"); on a node the player can see unless includeHidden says otherwise; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property. It looks every frame while a look is cheap and less often on a screen large enough for a look to cost a real share of a frame, so the game keeps its speed; the answer counts the frames the wait spanned and the looks it took, and says under looking_ms how long the looks took between them.", "type": "string" }, "signal": {
--- pinned/script_diagnostics +++ observed/script_diagnostics { - "description": "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", + "description": "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "A script inside the project, a folder, or a list of either, such as \"scripts/player.gd\" or [\"scripts/economy\", \"ui/shop.gd\"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was; several are answered together under scripts, each in that same shape, with clean, errors and warnings for all of them beside it, and a script that could not be read is named there with why while the rest are still asked about. At most 300 scripts a call.", + "description": "A script inside the project, a folder, or a list of either, such as \"scripts/player.gd\" or [\"scripts/economy\", \"ui/shop.gd\"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was. Several are answered together, with clean, errors and warnings for all of them: a script with nothing to report is named in nothingReported, and every other is answered under scripts in that same shape, including a script that could not be read, named with why while the rest are still asked about. Any number of scripts is one call, which reports progress as it goes.", "items": { "type": "string" },
--- pinned/script_info +++ observed/script_info { - "description": "What a script contains: its structure from the file, or symbols, completions and hover text from the editor's language server. Operations: structure: functions, variables, signals, class_name and extends, read from the file; symbols: document symbols from the language server; completion (needs line, character): completions at a position; hover (needs line, character): hover text at a position. Default structure.", + "description": "What a script contains: its structure from the file, or symbols, completions and hover text from the editor's language server. Operations: structure: functions, variables, signals, class_name and extends, read from the file; symbols: document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration; completion (needs line, character): completions at a position; hover (needs line, character): hover text at a position. Default structure.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "completion, hover: zero-based column.", - "minimum": 0, + "description": "completion, hover: the column, counted from 1 as the editor shows it, with a tab counted as one. symbols gives each name at the line and column hover is asked at.", + "minimum": 1, "type": "integer" }, "includeInherited": { - "description": "completion, hover: zero-based line.", - "minimum": 0, + "description": "completion, hover: the line, counted from 1 as the editor shows it.", + "minimum": 1, "type": "integer" }, "op": {
Changed 2026-10-08 13:14 UTC
3 descriptions rewritten.
--- pinned/editor_launch +++ observed/editor_launch { - "description": "Opens the Godot editor on a project, or restarts the one already connected. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back. Operations: open (needs projectPath): open the editor on a project; restart: restart the connected editor and wait for it. Default open.", + "description": "Opens the Godot editor on a project, or restarts the one already connected. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back. Operations: open (needs projectPath): open the editor on a project; restart: restart the connected editor and wait for it. Default open.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/editor_status +++ observed/editor_status { - "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it; null when there is none. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", + "description": "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it; null when there is none. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs gdharness upgrade, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.", "inputSchema": { "additionalProperties": false, "properties": {},
--- pinned/script_diagnostics +++ observed/script_diagnostics { - "description": "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", + "description": "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "Script file inside the project, such as \"scripts/player.gd\".", - "type": "string" + "description": "A script inside the project, a folder, or a list of either, such as \"scripts/player.gd\" or [\"scripts/economy\", \"ui/shop.gd\"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was; several are answered together under scripts, each in that same shape, with clean, errors and warnings for all of them beside it, and a script that could not be read is named there with why while the rest are still asked about. At most 300 scripts a call.", + "items": { + "type": "string" + }, + "type": [ + "string", + "array" + ] } }, "required": [
Changed 2026-10-07 13:06 UTC
3 descriptions rewritten, 2 schema or annotation changes with the description left byte-identical.
--- pinned/editor_run +++ observed/editor_run { - "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does. Default start.", + "description": "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. What runs is a scene: a SceneTree script is not an entry point here, so put the script on the root of a scene of its own and name that in scene. args hands the game its own flags, the ones it reads with OS.get_cmdline_user_args(), and a run carrying any is started by this server rather than by the editor. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way. start and check both wait first for a scan the open editor is running and for the class cache it writes just after, since a game booted in between resolves no global class, as does every other engine this server starts on the project; waitedForEditorScanMs says how long, and scanNote says when a scan ran past thirty seconds and the game started during it. A start waits for the game to become something the runtime_* tools can talk to and says which it is under runtime: listening with the port it took, or why not, so the first call after a start does not have to be made twice. When it is not listening, mayYetAnnounce says whether that is final: false is a runtime that is not coming, true is a game still on its way up, which editor_status will see and runtimeWaitMs waits longer for, and the note then says what a runtime_* call naming no projectPath meets until it announces. A start this server made also answers with transcript, the file both the run's streams are written to, so a watch on it can be armed off the start rather than off a second call. Operations: start (needs projectPath): run the project until it quits or is stopped; stop: end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause; check (needs projectPath): boot headless, quit after a few frames, and report the verdict; wait: wait for the run to end, then answer as editor_output does. Default start.", "inputSchema": { "additionalProperties": false, "properties": {
--- pinned/project_test +++ observed/project_test { - "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. Needs gdUnit4 under addons/gdUnit4.", + "description": "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. Needs gdUnit4 under addons/gdUnit4.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "A test directory or suite file inside the project, or a list of them, all run on one engine: a named set of suites boots once rather than once per suite. An entry given twice, or a suite inside a directory also listed, runs once. A run where any entry is not there is refused before the engine starts, naming each one. Default test.", + "description": "A test directory or suite file inside the project, or a list of them, all run on one engine: a named set of suites boots once rather than once per suite. An entry given twice, or a suite inside a directory also listed, runs once. An entry that is not there is left out and named under missing, the rest run, and such a run is never called passed; when no entry is there the run is refused before the engine starts, naming each one. Default test.", "items": { "type": "string" },
--- pinned/runtime_input +++ observed/runtime_input { - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: the pointer ops answer with pointer_note there, because get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says \"the words\", or \"the middle of the label\" with a note saying why they could not be placed, and link_pressed names the meta the label reports clicked. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says \"the words\", or \"the middle of the label\" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", "inputSchema": { "additionalProperties": false, "properties": { - "description": "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one.", + "description": "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when another control says exactly the words (hidden, off the screen, covered, or on screen taking clicks without being a button), since it would stand in for that one, and when it lets the pointer through to what is under it; index presses it anyway. Words found only inside longer words (\"out\" in \"Scout\") rank after every other match and are refused unless index is given; words starting a longer word (\"Recruit\" in \"Recruits\") count. An empty field says its placeholder, and a control with no text of its own, a picture on a button, says its accessibility name. An item of an open menu is drawn by the menu rather than held as a control, so a click finds none, and the refusal names the menu and the choose call that picks it.", "type": "string" }, "shift": {
--- pinned/runtime_inspect +++ observed/runtime_inspect { "description": "Questions about the running game: what is written on the screen, the scene tree, the nodes matching a query, where one node is on screen, what one property reads, or the performance metrics. Needs the game running with the runtime addon. Operations: tree: the live scene tree; text: every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it. Every line reads as drawn: rich text without its tags, text only as far as it has been typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each; find: the paths of every node matching className, script, namePattern, group or says, with property read off each, hidden ones included unless includeHidden says otherwise; rect (needs nodePath): one node's rectangle or position, in canvas and in window pixels. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one; property (needs nodePath, property): what one property reads on a node, or through the objects it holds, refusing a name nothing along the way has; metrics: frame time, memory, draw calls and the rest. Default tree.", "inputSchema": { - "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that takes no arguments and only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.", + "description": "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, \"_game:clock:speed\", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, \"_game:run:roster:0:traits\", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A step written as a call, \"get_viewport():gui_get_focus_owner()\", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox(\"panel\"):content_margin_top' or 'get_theme_constant(\"separation\")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, \"_game:run:board:size()\" or \"_game:inbox:keys()\", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.", "type": "string" }, "says": { - "description": "find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively, which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words.", + "description": "find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules (\"*won*|*lost*\"); write \\| for a bar that is part of the words.", "type": "string" }, "script": {
--- pinned/runtime_invoke +++ observed/runtime_invoke { "description": "Sets a property or calls a method on a node in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors and warnings the engine reported while a called method ran come back under errors, the first 20, with errors_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes. Operations: set (needs nodePath, property, value): set a property, on a node or on an object it holds; call (needs nodePath, method): call a method, on a node or on an object it holds, and return its result; result (needs requestId): collect the reply to a set or call whose wait ran out, by the requestId it answered with.", "inputSchema": { - "description": "call: the arguments, fitted to the method's parameter types. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths.", + "description": "call: the arguments, fitted to the method's parameter types. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, \"_game:run:wares:3\", or one starting at a node, \"/root/Main/Hud\" or \"/root/Main:_game:run\". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused.", "type": "array" }, "method": { - "description": "call: which one to call. Colons call through what a node holds, \"_game:run:advance\", the same way a property is written through them, a list or a map is stepped into by index or key, \"_game:run:roster:0:retire\", and a step written as a call walks through what a method taking no arguments returned, \"get_viewport():gui_get_focus_owner\".", + "description": "call: which one to call. Colons call through what a node holds, \"_game:run:advance\", the same way a property is written through them, a list or a map is stepped into by index or key, \"_game:run:roster:0:retire\", and a step written as a call walks through what a reading method returned, \"get_viewport():gui_get_focus_owner\", as runtime_inspect property describes.", "type": "string" }, "nodePath": { + "properties": { + "description": "call: read the object the call answered with in the same answer, under result_properties: true for every variable its script declares, or a list of names, each a colon path walked from that object the way property paths are (\"dead_allowed\", \"terms:0:name\"). A name that is not there is listed under result_properties_missing with why, and a result that is not an object answers properties_note instead. Without it an object comes back as its class and id only.", + "items": { + "type": "string" + }, + "type": [ + "boolean", + "array" + ] + }, "property": { - "description": "set: which one to write. Colons write through what a node holds, \"_game:run:day\", and a number or a key steps into a list or a map on the way, \"_game:run:roster:0:name\", a negative number counting from the end, and a step written as a call, \"get_viewport():gui_embed_subwindows\", walks through what a method taking no arguments returned. A map keyed by numbers is stepped into by the number, \"_game:units:3\". The answer reads back off the same holder, and a write that leaves the property as it was is refused and says what it still holds: the engine not taking it and a setter keeping the value look the same, so the refusal names neither. A write of the value the property already holds, at its own precision, as a float given in 64 bits to a property keeping 32, is not refused: it answers unchanged: true.", + "description": "set: which one to write. Colons write through what a node holds, \"_game:run:day\", and a number or a key steps into a list or a map on the way, \"_game:run:roster:0:name\", a negative number counting from the end, and a step written as a call, \"get_viewport():gui_embed_subwindows\", walks through what a reading method returned, as runtime_inspect property describes. A map keyed by numbers is stepped into by the number, \"_game:units:3\". The answer reads back off the same holder, and a write that leaves the property as it was is refused and says what it still holds: the engine not taking it and a setter keeping the value look the same, so the refusal names neither. A write of the value the property already holds, at its own precision, as a float given in 64 bits to a property keeping 32, is not refused: it answers unchanged: true.", "type": "string" }, "requestId": {
Changed 2026-10-06 13:09 UTC
1 description rewritten.
--- pinned/runtime_input +++ observed/runtime_input { - "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: the pointer ops answer with pointer_note there, because get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", + "description": "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button or mouse motion. All of it works headless, where the window is 64 by 64 and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: the pointer ops answer with pointer_note there, because get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. Operations: click: press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says \"the words\", or \"the middle of the label\" with a note saying why they could not be placed, and link_pressed names the meta the label reports clicked. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room; choose (needs nodePath): take an item out of a menu, by what it says or by where it is in the list. A menu's items are drawn rather than built, so there is nothing to click: the menu is opened, the item is looked for in it as it stands then, since a game can fill a menu as it opens, and the item takes the focus and ui_accept presses it, which is the engine's own path, needs no window, and does not depend on what ui_accept is bound to. A disabled or hidden button in front of the menu is refused, and so is a press the menu did not take, which is heard rather than assumed. Answers with what was chosen, read before the press, what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick), and what the button in front of it shows now when it is still there; action (needs action): press an action, or hold it; key (needs keycode): press a key, or hold it. Not for a menu a click has opened: an OptionButton or PopupMenu pops up as a window of its own that a key sent to the game never reaches, so an arrow moves nothing in it and Enter closes it with nothing chosen. choose is what selects there. The limit is that menu's own navigation, not windows in general: an embedded dialog whose owner reads keys in _input gets them, and a binding catcher up in one takes the key first time; text (needs text): type a string into the field being edited, a character at a time, and say what it landed in and what it holds afterwards, a secret field as its mask. The field is the focus of the dialog that has the focus when one does, which is where the keys go; mouse_click (needs x, y): click a mouse button at a position, pressed and released a frame apart, each event carrying the buttons held once it has happened; the answer says whole. A drag is a press held with pressed true, motions, and a release with pressed false. A wheel button is always a whole step, press and release together as a mouse sends one: a lone wheel press holds the viewport on the control that took it, so every click after it lands there; mouse_motion (needs x, y): move the mouse to a position, carrying the distance from where the last injected event put it unless relativeX and relativeY say otherwise, and the buttons held, so a control that drags moves under a run of these between a held mouse_click and its release.", "inputSchema": { "additionalProperties": false, "properties": {
Current tools
Show all 30 tool fingerprints
Watch this server yourself
If you run this server, put the proxy in front of it. It pins these exact fingerprints on first connect and stops the session if they move.
npx -y mcp-pin@0.2.3 -- <your gdharness command>
Or subscribe to this page's RSS feed to be told when it changes.
Badge
The badge states one fact about time and nothing else. It never claims a server is safe.
[](https://mcp-pin.gautamkhosla.com/servers/ae422cf00ccdc7c0.html)