Look up RunAPI pricing for the gpt-image-2 model line.

Endpoint name. Defaults to the endpoint that offers the model.

Model slug. Defaults to the line's primary model.

Create a GPT Image 2 task on RunAPI (edit image). Returns a task id, status, and output URLs.

RunAPI model slug for this model line.

Poll until the task reaches a terminal status.

Fetch the current status and latest result payload for a gpt-image-2 task.

Asynchronous endpoint the task was created on.

Task id returned when the task was created.

login

Authenticate RunAPI by opening a browser PKCE login flow and saving the API key to ~/.config/runapi/config.json.

Re-run browser login when the current credential comes from the local config file.

Create a GPT Image 2 task on RunAPI (text to image). Returns a task id, status, and output URLs.

RunAPI model slug for this model line.


Adds a comment to an existing Linear issue. Supports markdown formatting in the comment body. Can optionally specify a custom user name and avatar for the comment. Returns the created comment's details including its URL.

Comment text in markdown format

Optional custom username to show for the comment

Optional avatar URL for the comment

ID of the issue to comment on

Creates a new Linear issue with specified details. Use this to create tickets for tasks, bugs, or feature requests. Returns the created issue's identifier and URL. Required fields are title and teamId, with optional description, priority (0-4, where 0 is no priority and 1 is urgent), and status.

Issue description

Priority (0-4)

Issue status

Team ID

Issue title

Retrieves issues assigned to a specific user or the authenticated user if no userId is provided. Returns issues sorted by last updated, including priority, status, and other metadata. Useful for finding a user's workload or tracking assigned tasks.

Include archived issues in results

Maximum number of issues to return (default: 50)

Optional user ID. If not provided, returns authenticated user's issues

Searches Linear issues using flexible criteria. Supports filtering by any combination of: title/description text, team, status, assignee, labels, priority (1=urgent, 2=high, 3=normal, 4=low), and estimate. Returns up to 10 issues by default (configurable via limit).

Filter by assignee's user ID

Filter by estimate points

Include archived issues in results (default: false)

Filter by label names

Max results to return (default: 10)

Filter by priority (1=urgent, 2=high, 3=normal, 4=low)

Optional text to search in title and description

Filter by status name (e.g., 'In Progress', 'Done')

Filter by team ID

Updates an existing Linear issue's properties. Use this to modify issue details like title, description, priority, or status. Requires the issue ID and accepts any combination of updatable fields. Returns the updated issue's identifier and URL.

New description

Issue ID

New priority (0-4)

New status

New title


Look up RunAPI pricing for the gpt-4o-image model line.

Endpoint name. Defaults to the endpoint that offers the model.

Model slug. Defaults to the line's primary model.

Fetch the current status and latest result payload for a gpt-4o-image task.

Asynchronous endpoint the task was created on. Defaults to the line's only asynchronous endpoint.

Task id returned when the task was created.

Authenticate RunAPI by opening a browser PKCE login flow and saving the API key to ~/.config/runapi/config.json.

Re-run browser login when the current credential comes from the local config file.

Create a GPT-4o Image task on RunAPI (text to image). Returns a task id, status, and output URLs.

RunAPI model slug for this model line.

Poll until the task reaches a terminal status.


Animation (edit)

Create and modify animations on an AnimationPlayer and preview them in the editor: create, delete, or update animations, add and remove tracks and keyframes, backfill RESET keys for existing tracks (create_reset_keys), and play, stop, or seek the editor's preview (playback controls the editor, not the running game). Pair each change with an immediate play or seek to check the result; this is the only way to verify animation feel without running the whole game. To inspect animation data without changing it, use godot_animation_read.

play: Play an animation
stop: Stop playback. Godot leaves the first keyframe values on the nodes unless the player has a RESET animation; the reply warns when it does not.
seek: Seek to a position in the current animation
create: Create an animation
delete: Delete an animation
update_props: Update animation properties
add_track: Add a track to an animation
create_reset_keys: Backfill RESET keys for existing tracks: every track on one animation (or all of the player's animations) gets a RESET key holding the property's current value where RESET lacks one. Use it on tracks authored by hand or before add_track keyed RESET automatically, and call it BEFORE previewing, while the nodes still hold their rest pose.
remove_track: Remove a track
add_keyframe: Add a keyframe to a track
remove_keyframe: Remove a keyframe
update_keyframe: Update a keyframe

for play: Animation name; for create: Animation name; for delete: Animation name; for update_props: Animation name; for add_track: Animation name; for create_reset_keys: Animation to cover (default: all animations on the player except RESET); for remove_track: Animation name; for add_keyframe: Animation name; for remove_keyframe: Animation name; for update_keyframe: Animation name (required for: play, create, delete, update_props, add_track, remove_track, add_keyframe, remove_keyframe, update_keyframe; optional for: create_reset_keys)

Method arguments (for: add_keyframe)

Also key the property's current value into the player's RESET animation (created if missing), as the editor's track panel does, so previews are undone by RESET and save writes the rest pose. Default true. (for: add_track)

Custom blend time, -1 for default (for: play)

Playback speed, 1.0 default (for: play)

Play from end for reverse (for: play)

Track index to insert at, -1 for end (for: add_track)

Keep the current pose instead of seeking to the first keyframe (default false) (for: stop)

Keyframe index (required for: remove_keyframe, update_keyframe)

Animation length in seconds (for: create, update_props)

Library name (for: create, delete)

Loop mode: none, linear, pingpong (for: create, update_props)

Method name for method tracks (for: add_keyframe)

Path to the AnimationPlayer

Position to seek to (required for: seek)

Step value for keyframe snapping (for: create, update_props)

Keyframe time in seconds (required for: add_keyframe; optional for: update_keyframe)

Track index (required for: remove_track, add_keyframe, remove_keyframe, update_keyframe)

Node path and property, e.g. "Sprite2D:frame" (required for: add_track)

Type of track (required for: add_track)

Transition curve, 1.0 = linear (for: add_keyframe, update_keyframe)

Update node immediately, default true (for: seek)

Keyframe value, typed to match the track's target property: numbers and strings as-is; Color as {r, g, b, a}; Vector2 as {x, y}; Vector3 as {x, y, z}; Quaternion/Vector4 as {x, y, z, w} (the same shape get_keyframes and get_properties emit). A bare numeric array of the right length is accepted for those types; any other mismatch is rejected with TYPE_MISMATCH rather than stored and coerced to black/zero at play time. (for: add_keyframe, update_keyframe)

Animation (read)

Inspect animation data on AnimationPlayer nodes in the editor: list players in the scene, read a player's state and libraries, get an animation's tracks and properties, and read a track's keyframes. Reach for it to verify what the editor actually loaded, including after editing animation resources by hand. It changes and previews nothing; use godot_animation_edit to create, modify, or play animations.

list_players: List AnimationPlayer nodes in the scene
get_info: Get AnimationPlayer state and library list
get_details: Get an animation's tracks and properties
get_keyframes: Get keyframes for a track

Animation name (required for: get_details, get_keyframes)

Path to the AnimationPlayer (required for: get_info, get_details, get_keyframes)

Starting node path (defaults to scene root) (for: list_players)

Track index (required for: get_keyframes)

Godot Docs

Fetch Godot Engine documentation. Use fetch_class for class references (e.g. CharacterBody2D), fetch_page for tutorials/guides. Auto-detects Godot version from editor connection. Returns clean markdown.

fetch_class: Get a class reference by name
fetch_page: Get any docs page by path

Class name to fetch, e.g. "CharacterBody2D" (required for: fetch_class)

Documentation path, e.g. "/tutorials/2d/2d_movement.html" (required for: fetch_page)

Which class-reference section to return (default: full). Use specific sections to reduce token usage. (for: fetch_class)

full description properties methods signals

Godot docs version. If omitted, auto-detects from connected Godot editor or defaults to "stable"

Editor Control (edit)

Drive the editor: select a node, run or stop the project, restart the editor, rescan the filesystem to import assets written outside the editor, and center/zoom the 2D viewport. Use run with frozen=true as the deterministic-playtest entry point (game time holds at frame 0 until godot_game_time steps or thaws it). To test edited gameplay scripts just stop then run — the launched game loads .gd/.tscn fresh from disk; reserve restart for EDITOR-side staleness (edited @tool/addon code, a stale project.godot, or a cached .gdshader). For observation only (state, selection, logs, screenshots) use godot_editor_read instead; restart does not start a cold editor, so one must already be running.

select: Select a node in the editor
run: Run the project
stop: Stop the running project
restart: Restart the editor, reloading project.godot (autoloads, input map), addon code, and plugins from disk. Use it for EDITOR-side staleness only: edited @tool/addon/plugin code, changed autoloads or input map, or a .gdshader the editor still renders from a cached compile. NOT needed to test edited gameplay scripts — a launched game loads .gd/.tscn fresh from disk, so godot_editor_edit stop then run already runs the new code. Fire-and-forget: the bridge drops and auto-reconnects within a few seconds. Does not start a cold editor - the editor must already be running.
rescan: Rescan the project filesystem so the editor imports assets written to disk outside the editor (textures, audio, fonts, .tres) without a restart. Do this after writing a new asset and before opening or reloading a scene that references it; otherwise the resource loads empty because no .import exists yet. Returns once the scan and its imports have finished.
set_viewport_2d: Center and/or zoom the 2D editor viewport. Pass at least one parameter; omitted parameters PRESERVE the current view (e.g. pass only zoom to zoom in on the current center). The addon reads the live viewport transform to fill in whatever you leave out.

select run stop restart rescan set_viewport_2d

X coordinate to center the 2D viewport on (omitted = keep current X) (for: set_viewport_2d)

Y coordinate to center the 2D viewport on (omitted = keep current Y) (for: set_viewport_2d)

Launch with game time frozen from frame 0 (gameplay never starts racing your latency). Use godot_game_time step/thaw to advance. (for: run)

Path to node to select (required for: select)

res:// paths of existing assets to force through reimport after the scan (for assets whose bytes changed on disk). (for: rescan)

Save the project before restarting (default: true). Set false to discard unsaved editor changes. (for: restart)

Scene to run (optional, defaults to main scene) (for: run)

Zoom level, e.g. 1.0 = 100%, 2.0 = 200% (omitted = keep current zoom) (for: set_viewport_2d)

Editor Control (read)

Observe the editor and running game: get editor state (open scene, play state, camera, viewport), read the current node selection, pull editor log messages (with an incremental cursor) and stack traces, and capture lossless PNG screenshots of the running game or an editor viewport. Reach for it to check what the editor sees before and after a change; screenshot_game needs a running game, while every other action works in the bare editor. It changes nothing - to select nodes, run/stop/restart, or move the 2D viewport use godot_editor_edit; errors from the running game (not the editor process) come via minimal-godot-mcp's get_console_output when that companion server is installed.

get_state: Get editor state: current scene, play state, version, camera, viewport
get_selection: Get the currently selected nodes
get_log_messages: Get errors and warnings from the EDITOR process - @tool script runtime errors, import failures, addon errors, and failures from editor-side operations (scene/resource edits, reloads). This is the feedback channel for editor-side changes: run it after a mutation to confirm it did not break the editor. It does NOT include errors from the running game - for those, use minimal-godot-mcp's get_console_output (game console via DAP). Filter by severity (errors-only = "did my change break the editor?") and use `since`/`cursor` to read only what is new; every response returns the current `cursor`, even when empty.
get_stack_trace: Get the most recent error stack trace
screenshot_game: Capture a lossless PNG of the running game. Each frame persists in context every later turn and never decays, so reserve it for genuine APPEARANCE judgments (spacing, color, art, "does it look right"). For STRUCTURE or state — which control is focused, a label's text, whether a panel is visible, a node's anchors/size — read cheap text instead: godot_node_read (scene tree, node properties) or godot_runtime_state digest (live values), both ~free versus the hundreds of visual tokens a frame costs. Do not re-shoot a view that has not changed.
screenshot_editor: Capture a lossless PNG of an editor viewport. Same context cost as screenshot_game — the frame persists every later turn — so capture for appearance, not for structure/state you could read as cheap text via godot_node_read (scene tree, node properties) or godot_runtime_state.

Clear the editor error buffer after reading (for: get_log_messages)

Maximum number of messages to return (default: 50) (for: get_log_messages)

for screenshot_game: Maximum width in pixels (default: 900). Cost scales with resolution (~1 visual token per 28x28px patch; a 900px 16:9 frame ≈ 600 tokens, a native 1080p frame ≈ 2700 on Opus). 640 is the legibility floor for chip-dense UI — still crisp; 512 is the edge and 384 breaks fine print — so drop toward 640 to roughly halve per-frame cost when you do not need the finest text, and raise above 900 only when detail is genuinely unreadable.; for screenshot_editor: Maximum width in pixels (default: 900). Cost scales with resolution (~1 visual token per 28x28px patch; a 900px 16:9 frame ≈ 600 tokens). 640 is the legibility floor for chip-dense UI (512 is the edge, 384 breaks fine print), so drop toward 640 to roughly halve per-frame cost when you do not need the finest text; raise above 900 only when detail is unreadable. (for: screenshot_game, screenshot_editor)

Filter by severity: "error" drops warnings (the "did anything actually break?" check), "warning" returns only warnings, "all" (default) returns both. (for: get_log_messages)

Return only messages newer than this cursor. Pass back the `cursor` from a prior response to see just what is new since then - the incremental check that avoids re-reading the whole buffer (0/omitted = from the beginning). (for: get_log_messages)

Which editor viewport to capture (for: screenshot_editor)

Game Script Execution

Execute GDScript inside the RUNNING game process — the scenario-setup primitive: grant weapons, skip waves, spawn entities, arm persistent test bots, without baking debug hooks into game code. Errors when no game is running. For launch-time setup, compose: godot_editor_edit run frozen=true -> godot_exec run (mutate state, attach bots under `holder`) -> godot_game_time thaw. A static denylist rejects accidental process/file-write escape (OS.execute, DirAccess, write-mode FileAccess, ResourceSaver, ProjectSettings.save, ...) and names the offending token — an accident guard, NOT a security boundary. Compile errors reject the call with the parser message; runtime errors come back in runtime_errors with the call still completing.

run: Run one-shot GDScript inside the running game and return its value
list: List the nodes currently attached under the exec holder (name, class, age, processing state)
remove: Remove one exec holder child by name (queue_free)
clear: Remove every exec holder child (queue_free all)

Wall-clock patience for the call, used to size the timeout cascade (default 10000, max 30000). NOT enforcement: a synchronous script cannot be preempted, so an infinite loop hangs the game past any budget — recover with godot_editor_edit stop. (for: run)

Node name as reported by list (required for: remove)

GDScript statements, compiled as a function body. In scope: every autoload by its own name (e.g. `G.wave = 5`), `tree` (SceneTree), `root` (root Window) — the same context as step_until predicates — plus `holder`, a Node that survives scene reloads: attach nodes under it for behavior that persists between tool calls (a Timer-driven guard, an autofire bot), then manage them with list/remove/clear. Holder children pause with the tree, so a bot armed under a freeze acts only after thaw/step. Use an explicit `return` to get a value back (there is no implicit return): primitives (bool/int/float/String) come back intact; any other type (Array/Dictionary/Object/Vector2) comes back as a str() preview TRUNCATED to 200 chars — return JSON.stringify(...) yourself when you need structure. print() output is not returned — use return values (or, when the minimal-godot-mcp companion server is installed, its get_console_output). Function bodies cannot declare top-level func/class — use lambdas for callbacks, or build a sub-script with GDScript.new() and set_script() it onto a holder child for _process-driven behavior. No `await` (synchronous-only; compose with godot_game_time to wait). A runtime error or failed assert() breaks the game into the editor debugger mid-call; the relay auto-resumes it and the error comes back in runtime_errors (any debugger break in the call window is resumed, including a breakpoint hit by unrelated game code). (required for: run)

Game Time Control

Make game time answer to your clock instead of racing ahead between tool calls: freeze the running game, observe it at leisure (screenshots and state digests work while frozen), then step forward a bounded slice of game time (step) — or until a condition you specify holds (step_until) — with inputs riding inside the window. The game's own pause menu is layered correctly: freezing over it, stepping under it, and thawing back to it all preserve the game's pause intent.

freeze: Pause the game under agent control. All observation tools (screenshots, runtime state, godot_node_read find) keep working while frozen — take as long as you need.
step: Advance a bounded slice of game time, then re-freeze. Freezes first if the game is running, so step is always a safe first call. Pass exactly one of duration_ms or frames.
step_until: Advance game time until a GDScript predicate becomes true (or a safety cap is hit), then re-freeze. Freezes first if the game is running, so it is a safe first call. Use this instead of step when you do not know how long to advance to reach the state worth observing.
thaw: Resume real-time play, restoring the game's own pause state (an open pause menu stays open).
status: Report the freeze state: `frozen` is authoritative for the current state; `frozen_wall_ms` is real wall-clock held (not game time); `launched_frozen` is a historical flag (this run booted frozen) and stays true after thaw. Also reports the game's own pause intent, time scale, and whether game code is contesting the freeze.

Game time to advance in milliseconds (max 50000; loop steps for longer). Scaled by Engine.time_scale like normal play. Mutually exclusive with frames. (for: step)

Frames to advance instead of a duration (max 1200). frames: 1 is a single-frame advance. Mutually exclusive with duration_ms. (for: step)

for step: Input timeline executed inside the window; start_ms is game time from window start. Entries share the godot_input sequence vocabulary: named actions (with analog strength), joypad buttons, axis holds, stick vectors, raw keys (with modifier combos), and relative mouse-look (look: [dx, dy], delivered as InputEventMouseMotion.relative inside the frozen step — the FPS-camera testing path). Inputs must ride inside the step — events injected while frozen miss their is_action_just_pressed edge. Holds are always released by window end.; for step_until: Optional input timeline driven inside the window, exactly like step (same vocabulary: actions, joypad buttons, axes, stick vectors, raw keys, relative mouse-look look:[dx,dy] — e.g. hold a stick deflection while waiting for an enemy to appear). Holds are released by window end. (for: step, step_until)

The input action name from the project Input Map

How long to hold the input (0 = instant tap; axes return to 0 at the end)

When to start the input (milliseconds from sequence start)

Analog press strength (default 1.0). Drives Input.get_action_strength/get_vector even for actions with no joypad bindings, but BYPASSES the InputMap deadzone — use an axis entry when real deadzone behavior matters.

Joypad device id (default 0). InputMap bindings use device -1 (all devices) by default, which any id matches.

Joypad button by Godot JoyButton name (e.g. "a", "right_shoulder", "dpad_up") or raw index. Drives bound actions, raw _input handlers, and polled Input.is_joy_button_pressed.

Joypad axis by Godot JoyAxis name. Sticks range -1..1 and rest at 0; triggers range 0..1 and rest at 0.

Axis value held for duration_ms, then returned to 0. Drives axis-bound actions with REAL InputMap deadzone math, raw _input handlers, and polled Input.get_joy_axis.

Stick vector shorthand: compiles into a paired _x/_y axis hold with the same timing.

Stick x deflection: -1 = left, +1 = right.

Stick y deflection in Godot joypad convention: -1 = UP, +1 = down. Aim 30 degrees above horizontal-right = {x: 0.866, y: -0.5}; half-tilt right = {x: 0.5, y: 0}.

Raw keyboard key by name with optional modifier prefixes ("a", "escape", "ctrl+s", "shift+alt+f1"), or a raw Godot Key enum int. Modifiers: ctrl, shift, alt, meta (aka cmd/super). Drives InputMap key bindings, raw _input/_unhandled_input handlers, and polled Input.is_key_pressed; each modifier is pressed as a real key so Input.is_key_pressed(KEY_CTRL) also holds (so injecting "ctrl+s" also fires any action bound to bare Ctrl, like a real keyboard). For typing into focused text fields use type_text instead.

Target the physical key position (layout-independent): sends a physical-keycode-only event for Input.is_physical_key_pressed and physical-keycode InputMap bindings. Default false sends both logical and physical keycodes (a US-layout key press). WARNING: with physical:true the logical keycode is unset, so keycode-bound InputMap actions will NOT match — use the default for those.

Relative mouse-look: a mouse movement of [dx, dy] SCREEN pixels (+x = right, +y = down), injected as InputEventMouseMotion to drive FPS-camera code that integrates event.relative in _input/_unhandled_input. The camera sees event.relative == [dx, dy] in projects with no 2D stretch (the default, and typical for a 3D FPS); a 2D content-scale stretch scales it, exactly as a physical mouse would. duration_ms 0 (default) snaps the whole delta in one event; duration_ms >= 16 distributes it as a smooth multi-event sweep (~16ms/chunk = 60Hz, up to 256 sub-events) summing to the delta within float32 precision; a shorter non-zero duration collapses to a single snap. This is RELATIVE motion, never cursor positioning (absolute mouse coordinates remain unsupported — see docs/design/mouse-input-spike.md). The game owns Input.mouse_mode (set MOUSE_MODE_CAPTURED via godot_exec for capture-gated cameras).

Safety cap on game time to advance while waiting (max 50000, default 20000). If the predicate never holds within this budget (or the wall-clock budget), the call returns with predicate_met: false instead of hanging. (for: step_until)

for step: Optional GDScript expressions evaluated on the window's last frame and returned as a { expression: value } map, exactly as for step_until (same scope: autoloads by name, `tree`, `root`, engine singletons). Reads what the inputs did in the same call instead of a follow-up exec.; for step_until: Optional GDScript expressions (same scope as `until`) evaluated when stepping stops and returned as a { expression: value } map — e.g. ["G.wave", "tree.get_nodes_in_group(\"enemies\").size()"]. Use this to read the state you care about in the same call instead of a separate observation round-trip. Each must parse up front, but is only evaluated when stepping stops, so it may read state the step itself creates (a spawned node); an expression that fails then comes back as { error: message } in the map without failing the call. Game code can tell it is inside a step window via `get_node("/root/MCPGameBridge").is_stepping()`. (for: step, step_until)

A GDScript boolean expression, re-evaluated against the running game every frame; stepping stops the frame it is truthy. Autoloads are in scope by name (e.g. `G.wave > 1`, `GameState.tick_count % 12 == 11`), plus `tree` (the SceneTree), `root` (the root Window) for tree queries (e.g. `tree.get_nodes_in_group("enemies").size() >= 1`), and the engine singletons `Engine`, `Time`, `Performance`, `AudioServer`, `Input`, `DisplayServer`, `OS`. Must parse and evaluate without error against the current state or the call is rejected up front. Expressions do NOT short-circuit (`and`/`or` evaluate both operands), so to read a node that may not exist yet, do not guard with `arr.size() > 0 and arr[0].x` — sequence two calls instead: step_until the node exists (`tree.get_nodes_in_group("boss").size() >= 1`), then step_until reading it (`tree.get_nodes_in_group("boss")[0].state == 4`). (required for: step_until)

GridMap (edit)

Modify GridMap cells in the open scene: set a single cell to a MeshLibrary item with an orientation, clear a cell, clear the whole map, or set many cells in one batch. Use it to build or remove 3D grid content; cell data is stored base64-encoded in the .tscn, so editing the file is not an alternative, and set_cells_batch beats repeated set_cell calls for anything beyond a few cells. To inspect the map, its MeshLibrary, or existing cells without changing anything, use godot_gridmap_read.

set_cell: Set a single cell
clear_cell: Clear a single cell
clear: Clear all cells
set_cells_batch: Set many cells at once

Array of cells to set (required for: set_cells_batch)

Cell coordinates (required for: set_cell, clear_cell)

MeshLibrary item index (required for: set_cell)

Path to the GridMap

Orientation 0-23, default 0 (for: set_cell)

GridMap (read)

Inspect GridMap data in the open scene: list GridMap nodes, get map and MeshLibrary info, and read used cells, a single cell, or every cell using a given item. Use it whenever you need to know which mesh items occupy which 3D grid cells; cell data is stored base64-encoded in the .tscn, so reading the file is not an alternative. To place or clear cells, use godot_gridmap_edit instead.

list: List GridMap nodes in the scene
get_info: Get GridMap info
get_meshlib_info: Get the GridMap's MeshLibrary info
get_used_cells: All used cells grouped by MeshLibrary item: items[] of {item, count, cells: [[x, y, z], ...]}. Orientation per cell is in get_cell / get_cells_by_item.
get_cell: Get a single cell
get_cells_by_item: Get all cells using a given MeshLibrary item

Cell coordinates (required for: get_cell)

MeshLibrary item index (required for: get_cells_by_item)

Path to the GridMap (required for: get_info, get_meshlib_info, get_used_cells, get_cell, get_cells_by_item)

Starting node path (defaults to scene root) (for: list)

Input Injection

Inject input into a running Godot game for testing: named actions (with analog strength), joypad buttons, analog axes, stick vectors, raw keyboard keys (with modifier combos), and relative mouse-look. Use get_map to discover available input actions and their bindings, sequence to execute inputs with precise timing (optionally with an effect probe that proves the inputs changed game state), or type_text to type into UI elements. Note: relative mouse-look is supported (look: [dx, dy], for FPS-camera _input handlers); absolute cursor positioning is not (see docs/design/mouse-input-spike.md).

get_map: List available input actions from the project Input Map
sequence: Execute an input timeline. While the game runs at real speed, keep tightly-timed inputs in ONE call — e.g. the run-starting menu press AND the gameplay that follows — because seconds of uncontrolled game time pass between two separate tool calls. For a window longer than one call can hold, drive input through godot_game_time step instead.
type_text: Type text into the focused UI element

Delay between keystrokes in milliseconds (default 50) (for: type_text)

Array of inputs to execute. Each entry is one of: a named ACTION (action_name, optional analog strength), a joypad BUTTON (joy_button), an analog AXIS hold (axis + value), a STICK vector (stick + x/y), a raw KEY (key, e.g. "ctrl+s"), or relative mouse LOOK (look: [dx, dy]) — mix freely on one timeline. Joypad events drive bound actions (with real deadzone math), raw _input handlers, and the polled Input singletons (get_joy_axis / is_joy_button_pressed); key events likewise drive bound actions, _input/_unhandled_input, and Input.is_key_pressed; look events deliver InputEventMouseMotion.relative to _input/_unhandled_input for FPS-camera code (duration_ms >= 16 distributes the delta as a smooth sweep). No physical pad, keyboard, or mouse is needed. Limitation: Input.get_connected_joypads() never reports a virtual pad, so games that gate controller mode on pad DETECTION cannot be switched into it. (required for: sequence)

The input action name from the project Input Map

How long to hold the input (0 = instant tap; axes return to 0 at the end)

When to start the input (milliseconds from sequence start)

Joypad device id (default 0). InputMap bindings use device -1 (all devices) by default, which any id matches.

Joypad axis by Godot JoyAxis name. Sticks range -1..1 and rest at 0; triggers range 0..1 and rest at 0.

Axis value held for duration_ms, then returned to 0. Drives axis-bound actions with REAL InputMap deadzone math, raw _input handlers, and polled Input.get_joy_axis.

Stick vector shorthand: compiles into a paired _x/_y axis hold with the same timing.

Stick x deflection: -1 = left, +1 = right.

Stick y deflection in Godot joypad convention: -1 = UP, +1 = down. Aim 30 degrees above horizontal-right = {x: 0.866, y: -0.5}; half-tilt right = {x: 0.5, y: 0}.

Optional effect probe: GDScript expressions evaluated once before the first input and again after the last, to prove the inputs actually changed something (vs. falling into the void — player dead, UI focus elsewhere, wrong action). Reference autoloads by name (e.g. "G.shots", "G.wave") plus `tree`/`root` (e.g. "tree.get_nodes_in_group('enemies').size()") and the engine singletons (Engine, Time, Performance, AudioServer, Input, DisplayServer, OS), same context as godot_game_time step_until. Each expression returns {before, after, changed}; the result also carries any_changed. Expressions do NOT short-circuit; a parse error rejects the call, while an expression that fails to evaluate reads as { error: message } on that side of the diff. The after-reading is sampled a couple frames past the final input, so only near-immediate effects register — for slower effects use godot_game_time or runtime_state watch. (for: sequence)

Optional: millisecond offsets (from sequence start) at which to capture a lossless PNG frame DURING the real-time run. The bridge owns the sequence clock, so it grabs each frame at the right moment and returns them with the result — letting you catch transient visuals (muzzle flashes, explosions, kill banners) that fade long before a separate screenshot call could land. Up to 8 frames, each returned as an image labeled with its actual offset; an offset (like the whole sequence) must fall within the 40000ms single-call window. COST: each frame is a SEPARATE image that persists in context every following turn and never decays; cost scales with resolution (~1 visual token per 28x28px patch), independent of format. Use multi-frame ONLY for transient/animated visuals — a static layout needs exactly ONE frame. Prefer a few frames at a modest screenshot_max_width over many large ones; for frozen/precise inspection use godot_game_time step + screenshot_game instead. (for: sequence)

Max width in px for captured frames (default 640). Resolution is the real cost lever (~1 visual token per 28x28px patch; a 640px frame ≈ 300 tokens). 640 catches transient visuals cheaply; raise it when you need to READ text/detail in the frame, lower it only for pure motion where legibility does not matter. (for: sequence)

Press Enter after typing to submit (for LineEdit text_submitted) (for: type_text)

Text to type (required for: type_text)

Node (edit)

Modify scene nodes in the editor: update a node's properties, or reparent it (the editor rewrites child paths and signal connections correctly; hand-editing .tscn for a reparent does not). Use it to change existing nodes in the open scene. To inspect properties, the scene tree, or search for nodes, use godot_node_read; to add or remove nodes, or attach scripts and connect signals, edit the .tscn file directly, then verify with godot_node_read's get_scene_tree.

update: Update a node's properties
reparent: Move a node to a new parent

Path to the new parent node (required for: reparent)

Path to the node

Properties to set on the node (required for: update)

Node (read)

Inspect scene nodes in the editor: read a node's effective properties (including class defaults a .tscn read cannot show), view the full scene tree as the editor sees it (including children inside instanced sub-scenes), and find nodes by name or type. Use it to discover node paths and verify the live state of the open scene before or after making changes. It cannot modify anything; to update properties or reparent a node, use godot_node_edit.

get_properties: Get a node's properties
get_scene_tree: Full hierarchy of the open scene as the editor sees it, including children inside instanced sub-scenes (a .tscn file read cannot show those). Deep or wide scenes can be large — cap the result with max_depth and/or max_children; any node whose children are cut off carries "truncated_children": <count of omitted direct children> instead of (or alongside) "children".
find: Find nodes by name and/or type. Searches the RUNNING game's live tree when a game is playing (spawned entities included); otherwise searches the scene open in the editor.

Cap how many children are listed per node. Omit to list every child. (for: get_scene_tree)

Cap recursion depth (root = depth 1). Omit for the full tree. (for: get_scene_tree)

Glob pattern to match node names, e.g. "*Spawner*", "Turret?" (for: find)

Path to the node (required for: get_properties)

Path to start search from (defaults to scene root). An absolute /root/... path may name an autoload or any node beside the scene. (for: find)

Filter by node type, e.g. "CharacterBody2D", "Area2D" (for: find)

Profiler

Profile a running game; every action errors if no game is playing. Use snapshot for one-shot engine metrics, or start → get_data for a per-frame time series with percentile stats, frame-budget usage, spike detection, and monitor trends. get_active_processes lists scripts with live _process/_physics_process callbacks across the whole tree, tagged scene/autoload/exec (useful for finding per-frame cost sources); get_signal_connections maps signal wiring, including an autoload's outgoing connections. get_data's per-frame detail is a ring of the last 300 frames; its run block covers the whole profile. For observing game state rather than performance, use godot_runtime_state.

snapshot: Full performance snapshot (all engine metrics)
start: Start per-frame time-series profiling
stop: Stop time-series profiling
get_data: Get collected time-series data with spike detection. Per-frame detail (percentiles, spikes, monitor trends) covers a ring buffer of the LAST 300 frames only — the `window` field says how much of the run that is; `run` carries whole-run aggregates (frames, duration, avg/max, frames over budget, a frame-time histogram) so a ten-second profile can still answer "did anything spike".
get_active_processes: List scripts with live _process/_physics_process callbacks across the whole tree — scene, autoloads, and nodes attached by godot_exec — tagged by location.
get_signal_connections: Inspect signal connections

Node to walk from (default: the whole tree — scene, autoloads and exec-attached nodes). An absolute /root/... path may name an autoload. (for: get_signal_connections)

Project Info

Read project-level data from the editor: name, path, Godot version, and main scene (get_info), plus project settings including input mappings (get_settings). After editing project.godot as a file, use check_stale to detect whether the editor is still running stale autoloads or input map from before the edit; restart via godot_editor_edit to reload. Use addon_status to diagnose addon/server version skew when commands misbehave or the connection drops; it also reports whether the running server build is stale relative to its source checkout. For scene contents or node properties, use godot_node_read instead.

get_info: Get project name, path, version, and main scene
get_settings: Get project settings
addon_status: Check addon/server version compatibility, and whether the running server build is stale (source edited after the last npm run build)
check_stale: Check whether project.godot was edited on disk after the editor loaded it, leaving the editor with stale autoloads / input map (and phantom "Identifier not found" errors in its log that do not exist at runtime). Returns the disk-vs-editor divergence; run godot_editor_edit restart to reload. Useful right after editing project.godot as a file.

Settings category to filter by (use "input" for input mappings) (for: get_settings)

Include built-in ui_* actions (with category="input") (for: get_settings)

Resource Inspector

Inspect a Resource file by path with type-aware structured output (SpriteFrames animations, TileSet, Material, Texture2D, etc.). Use it for imported or binary resources (.res, .scn, compressed textures) that a plain file read cannot parse, or when you want sub-resources resolved into the engine's view rather than raw .tres text. For nodes inside a scene, use godot_node_read instead.

get_info: Inspect a Resource file by path

Include internal properties starting with underscore (default: false)

Detail level: 0 = summary only, 1 = full detail (default), 2+ = expand sub-resources

Resource path (e.g., "res://player/sprites.tres")

Runtime State

Observe live game state as structured data. Use digest for a one-shot entity snapshot (replaces most godot_editor_read screenshot_game calls). Use watch_start → watch_collect for state-over-time without context blowup. Navigation (NavigationAgent path state, NavigationServer map sync, find-path) is not exposed here; use godot_exec with the recipe in docs/runtime-state-guide.md "Reading navigation state".

digest: Snapshot current game entity state as structured JSON — exact positions, velocities, animation state, and custom game data. Much cheaper than screenshot_game (no vision tokens). Works on any game with no setup; add nodes to the "mcp_watch" group or implement `func _mcp_state() -> Dictionary` on key nodes for richer, targeted data. WHAT TO PUT IN _mcp_state(): include BOTH (1) live runtime values that change during play (cursor position, health, score, fill counts) AND (2) static definition context an agent needs to interpret them — e.g. a puzzle node should expose its clue data, a level node its objective list, a shop its item catalog. Without definition context, an agent can observe state changes but cannot verify correctness. Also include layout geometry for renderable nodes (bounds, sizes, offsets) to enable programmatic layout checks without a screenshot.
watch_start: Start an in-engine sampler that records specified node fields over a time window. Returns immediately; call watch_collect after duration_ms to get the summarized digest. Field keys: "pos.x"/"pos.y"/"pos.z", "vel.x"/"vel.y"/"vel.z", "rot", "anim", "anim_frame" for Node2D/Node3D/bodies/animation nodes; "pos.x"/"pos.y"/"size.x"/"size.y" (post-layout global rect) for Controls; any key from _mcp_state() for custom game state (e.g. "health", "ammo"); or a plain numeric/String/bool property name. TIMING: watch_start and the action that drives state change (godot_input sequence, player input, etc.) must overlap within the watch window. Send both in the same parallel tool call batch, or use a duration_ms large enough (3000–4000ms) to cover the round-trip latency before the driving action is approved and sent. resolved_fields counts fields readable at start; every field that is not (node_not_found, not_readable = the key does not apply to that node type, field_cap) is listed in unresolved_fields with its reason.
watch_collect: Collect the current sampler buffer and return a per-field summary (start/end/min/max/mean/slope for numeric fields; transition events for string fields) plus a time-sorted `timeline` merging watched signal emissions with string-field transitions (kinds: signal, anim_transition, field_change). TIMESTAMPS: signal t_ms is emission time (ms resolution); anim/field t_ms is DETECTION time at the sample rate — the change happened up to one sample interval earlier, so do not infer cross-kind ordering from nearby timestamps. Safe to call before auto-stop — returns whatever has been recorded so far; signal connections stay live until the window ends.
watch_stop: Stop the sampler early (disconnecting watched signals) and return the final per-field summary and merged `timeline`. Equivalent to watch_collect + stopping the sampler.

Auto-stop after this many milliseconds (default: 1000) (for: watch_start)

Group name to use when select="group" or "auto" (default: "mcp_watch") (for: digest)

Sample rate in Hz (default: 20) (for: watch_start)

Subset of fields to include (default: all available). "ui" adds global_rect / visible_in_tree / has_focus / text on Controls. (for: digest)

transform velocity anim groups onscreen state ui

Maximum nodes in result (default: 40) (for: digest)

Glob filter on node name (e.g. "Player*") (for: digest)

Explicit absolute node paths to include in addition to tier selection, e.g. ["/root/GameState"]. The digest walks the current scene only, so autoload singletons — where global game state often lives (cash, score, settings) — are otherwise unreachable. Each path returns _mcp_state() if present, else a snapshot of the node's script variables (scalars/arrays, ~1 KB cap). Paths that do not resolve are returned in unresolved_paths. (for: digest)

Selection tier: "group" = nodes in mcp_watch group, "method" = nodes with _mcp_state(), "visible" = the visibility tier explicitly (visible 2D CanvasItems including runtime-spawned UI, AND 3D world nodes — meshes, gridmaps, cameras, lights, physics bodies and areas), "auto" = best available (default: auto picks group → method → the visibility tier as a fallback), "none" = no automatic selection; return only the nodes named in paths. Use "visible" with name/type filters to reach UI that is not in the group, e.g. select="visible", type="Label". (for: digest)

Signals to record as discrete timeline events during the window. Each emission is buffered as {t_ms, source, signal, args} (200 events/window shared FAIRLY — each signal gets an equal sub-budget so a chatty signal cannot starve a rare one; keep-first within each, with events_dropped + events_dropped_by_signal reporting any loss; args stringified to ~100 chars). watch_collect merges these with string-field transitions into a time-sorted `timeline`. Signals with more than 5 parameters are skipped and reported in unresolved_signals, as are bad paths/names. Connections stay live until duration_ms elapses or watch_stop. Signals must be emitted on the main thread (worker-thread emissions are unsupported). At least one of specs/signals is required. (for: watch_start)

Node path; absolute /root/... paths reach autoloads (e.g. "/root/G")

Signal name on that node — script or built-in (e.g. "body_entered")

Which nodes and fields to watch. Optional when signals is provided. (for: watch_start)

Field keys to sample (e.g. ["pos.x", "vel.x", "health"])

Full node path (e.g. "/root/Level/Player")

Class filter (e.g. "CharacterBody2D") (for: digest)

Scene

Manage scenes in the editor: open a scene, save the open scene, or reload an open scene from disk after you edit its .tscn directly (so the editor picks up the change without a full restart). To create a new scene, write the .tscn file directly — header [gd_scene format=3] without a uid (the editor assigns one when it imports the file), then one [node name="X" type="Node2D"] block per node — and open it with this tool.

open: Open a scene file
save: Save the currently active scene to its own file. Honors reset_on_save: players with a RESET animation are written at their rest pose, not a previewed one.
reload: Reload an open scene from disk, discarding the editor's unsaved in-memory copy. Use after editing a .tscn file directly to refresh the editor without a full restart; the scene must already be open.

for open: Path to scene file to open; for save: Must match the active scene's file (defaults to it); a scene with no file yet may be saved to a new path. Naming a different scene is rejected (SCENE_MISMATCH) instead of overwriting it with the active tab's content; for reload: Path of the open scene to reload (defaults to the current scene) (required for: open; optional for: save, reload)

3D Scene Info

Read engine-computed 3D spatial data that cannot be derived from .tscn text: global transforms resolved through the parent chain, mesh AABBs (VisualInstance3D, and GridMap folded from its placed cells), combined subtree bounds, and visibility. Use get_spatial_info for one Node3D or a filtered set of its children (by type or world-space region); use get_bounds for the combined AABB of a subtree. Read-only: to change transforms or other properties, use godot_node_edit.

get_spatial_info: Get spatial data for a Node3D and optionally its children
get_bounds: Get the combined AABB of a subtree (VisualInstance3D and GridMap nodes)

Include child nodes (for: get_spatial_info)

Limit number of results. Defaults to 50 when include_children=true. Set higher (e.g., 500) if needed. (for: get_spatial_info)

Path to the Node3D (required for: get_spatial_info)

Path to search root (defaults to scene root) (for: get_bounds)

Filter by node type, e.g. "MeshInstance3D" (for: get_spatial_info)

Only include nodes whose global position is within this AABB (for: get_spatial_info)

Min corner of the AABB

Size of the AABB

TileMapLayer (edit)

Modify TileMapLayer cells in the open scene: set a single cell, erase a cell, clear a whole layer, or set many cells in one batch. Use it to paint or remove tiles; cell data is stored base64-encoded in the .tscn, so editing the file is not an alternative, and set_cells_batch beats repeated set_cell calls for anything beyond a few tiles. To inspect layers, TileSets, or existing cells without changing anything, use godot_tilemap_read.

set_cell: Set a single cell
erase_cell: Erase a single cell
clear_layer: Clear all cells in the layer
set_cells_batch: Set many cells at once

Alternative tile ID, default 0 (for: set_cell)

Atlas coordinates, default 0,0 (for: set_cell)

Array of cells to set (required for: set_cells_batch)

Cell coordinates (required for: set_cell, erase_cell)

Path to the TileMapLayer

TileSet source ID, default 0 (for: set_cell)

TileMapLayer (read)

Inspect TileMapLayer data in the open scene: list layers, get layer and TileSet info, read used cells, a single cell, or a rectangular region, and convert between local positions and map coordinates. Use it whenever you need to know what tiles are placed where; cell data is stored base64-encoded in the .tscn, so reading the file is not an alternative. To place, erase, or clear tiles, use godot_tilemap_edit instead.

list_layers: List TileMapLayer nodes in the scene
get_info: Get TileMapLayer info
get_tileset_info: Get the layer's TileSet info
get_used_cells: All used cells grouped by tile: tiles[] of {source_id, atlas_coords, alternative_tile, count, cells: [[x, y], ...]}. Answers "where is tile T" in one call; a few bytes per cell.
get_cell: Get a single cell
get_cells_in_region: Cells within a rectangular region, grouped by tile exactly like get_used_cells.
convert_coords: Convert between local position and map coordinates

Cell coordinates (required for: get_cell)

Local position to convert to map coords (for: convert_coords)

Map coordinates to convert to local position (for: convert_coords)

Maximum corner of region (required for: get_cells_in_region)

Minimum corner of region (required for: get_cells_in_region)

Path to the TileMapLayer (required for: get_info, get_tileset_info, get_used_cells, get_cell, get_cells_in_region, convert_coords)

Starting node path (defaults to scene root) (for: list_layers)

Validate Meshes

Check every code-built (ArrayMesh) surface in the RUNNING game for silent data corruption that renders WITHOUT any error. Run this FIRST when rendering looks wrong and no error is reported anywhere: surfaces pure black or far too dark, floors/walls invisible or see-through, geometry visible from only one side, lighting that ignores light-energy changes, scenes that "tuning" cannot fix. Detects: inside-out or mixed triangle winding (Godot front faces wind clockwise — wrong winding = culled faces or inverted lighting normals), dropped triangles / orphaned vertices (e.g. SurfaceTool.append_from of an indexed mesh mixed with raw add_vertex silently discards most of the batch), degenerate UVs that make generate_tangents() emit garbage, and NaN/zero normals or tangents. Every finding includes its likely cause and the fix. Read-only and cheap. Walks the current scene only (meshes parented elsewhere under root are not seen). Also run it after writing or changing mesh-generation code — BEFORE spending time tuning lights or materials to rescue a "too dark" scene.

Cap on findings returned (default 25). The total count is always reported.


Run quality analysis locally using Codacy CLI.

  Use this tool to analyze the quality of a repository using Codacy command line.

  Common use cases:
  - When the user asks for an analysis of a repository
  - When the user asks to scan the code
  - When the user wants to analyze based on Codacy configuration
  - When the user wants to apply fixes based on Codacy configuration and analysis
  - When the user wants Codacy results immediately without waiting for the next scheduled analysis

  Common mistakes:
  - Using this tool to know the status of a repository
  - Using this tool without specifying the rootPath
  - Using this tool to know current results from Codacy. Use codacy_get_repository_with_analysis instead.
  - Filling the tool parameter when not asked specifically for it.

  - Using this tool for a organization other than the current one
  - Using this tool with the wrong organization name (if you are not sure, use the codacy_list_organizations tool to validate the organization name)

  - Using this tool without specifying the repository name
  - Using this tool for a repository other than the current one
  - Using this tool with the wrong repository name (if you are not sure, use the codacy_list_organization_repositories tool to validate the repository name)

Optional. Absolute path to the file to analyze, or directory to analyze (e.g., "src/"). If left empty, the analysis will be executed for the whole project.

Optional. Organization name or username that owns the repository on the Git provider.  Try to extract it from the repository's git remote URL using 'git remote -v' following these patterns:
- SSH format: 'git@github.com:{organization}/{repository}.git'
- HTTPS GitHub: 'https://github.com/{organization}/{repository}.git'
- HTTPS GitLab: 'https://gitlab.com/{organization}/{repository}.git'
- HTTPS BitBucket: 'https://bitbucket.org/{organization}/{repository}.git'
Do not use the README file to extract the organization name.

Optional. Organization's git provider: GitHub (gh), GitLab (gl) or BitBucket (bb). Accepted values: gh, gl, bb. Try to extract it from the repository's git remote URL using 'git remote -v'.

Optional. Repository name on the Git provider. Try to extract it from the repository's git remote URL using 'git remote -v', it should be something like this for gh:'https://github.com/<owner>/<repository>.git' for gl:'https://gitlab.com/<owner>/<repository>.git' for bb:'https://bitbucket.org/<owner>/<repository>.git'.

Root path to the project or repository in the local filesystem. This must be filled to ensure permissions are met for the tool to run.

Optional. Tool to use for the analysis. If left empty, it will use all available tools. Possible values: eslint, pmd, trivy, pylint, dartanalyzer

Install the Codacy CLI used for local analysis.

  Common use cases:
  - When the user tried to use Codacy CLI and it is not installed

  Common mistakes:

  - Using this tool for a organization other than the current one
  - Using this tool with the wrong organization name (if you are not sure, use the codacy_list_organizations tool to validate the organization name)

  - Using this tool without specifying the repository name
  - Using this tool for a repository other than the current one
  - Using this tool with the wrong repository name (if you are not sure, use the codacy_list_organization_repositories tool to validate the repository name)

Get the definition of a specific pattern.

  Common use cases:
  - When the user asks for the definition of a specific pattern
  - When the user asks for more information about an issue found by Codacy

  Common mistakes:
  - Using this tool with the wrong toolUuid
  - Using this tool with the wrong patternId

Pattern identifier

The identifier of the tool that the pattern belongs to.

List all code analysis tools available in Codacy.

Pagination cursor for next page of results

Maximum number of results to return (default 100, max 100)


Discover Zotero tools

Discover the available Zotero tools by keyword — useful for progressive disclosure when you do not want to load every tool definition up front (the code-execution-with-MCP pattern). Pass an optional `query` (matched against tool names, titles, and descriptions) and `detail` ("names" or "descriptions", default "descriptions"). Returns the matching `zotero_*` tools so you can pick the right one for a task. With no query, returns the full catalog.

How much to return (default "descriptions").

Keyword to match against tool names/titles/descriptions.

Discover Zotero tools

Annotate a PDF (highlights, notes)

Add or delete Zotero PDF annotations (highlights, underlines, notes), the same objects you create in the Zotero PDF reader. `action:"add"` needs `parent` (a regular item key OR a PDF attachment key) and `annotations`: each with `type` (highlight|note|underline, default highlight), `text` (the exact passage to highlight), optional `comment`, `color`, `page` (0-based page index). **You do not need page coordinates**: give the passage in `text` and it is located in the PDF and anchored to the exact lines it occupies, so quoting a passage is enough to highlight it. Pass `page` to disambiguate a passage that repeats, or `occurrence` to pick among repeats; pass `position` ({"pageIndex":N,"rects":[[x1,y1,x2,y2],...]} in PDF points, bottom-left origin) only to place a highlight yourself. `action:"delete"` trashes the annotations in `annotation_keys`. Writes go to the running Zotero desktop app for your personal library (via its connector protocol, or its local-API writes where available), otherwise to the cloud Web API.

Default "add".

Annotation keys to trash (action:"delete").

Annotations to add.

Reading-order character offset of the passage start on the page (refines sort_index).

Hex color, e.g. "#ffd400" (highlight default) or "#26a69a".

Comment attached to the annotation (markdown-ish plain text).

Which occurrence of `text` to anchor when the passage appears more than once (1-based, in reading order). Only needed when a first attempt reports an ambiguous passage.

0-based PDF page index (annotationPageLabel uses page+1 when unset).

Page height in points (refines sort_index, e.g. 841.89 for A4).

Explicit page label (overrides page+1).

Zotero position: {"pageIndex": N, "rects": [[x1,y1,x2,y2],...]} (points, bottom-left origin), its JSON string, or shorthand [N, [x1,y1,x2,y2]]. Optional: when omitted, the passage in `text` is located in the PDF and its coordinates are computed for you.

Explicit annotationSortIndex; computed from position when omitted.

Tags to attach to the annotation.

The highlighted/underlined passage itself (required for highlight/underline). For notes, goes in annotationText of an extracted-text style note or leave to `comment`.

Annotation type; default "highlight".

Item key or PDF attachment key to annotate.

Annotate a PDF (highlights, notes)

Attach a file (PDF, snapshot) to an item

Add a stored file attachment (e.g. a PDF full text) under an existing item. Give `parent` (the item key) and either `url` (Zoteus downloads it, then stores it) or `path` (a file on the machine running Zoteus). `filename` and `content_type` are inferred when omitted. Saves through the Zotero desktop app when one is reachable (Zotero 10+ local API; you may be asked once to allow Zoteus write access, choose "Always Allow"), and otherwise through the cloud Web API, which needs ZOTERO_API_KEY with file access and uses your Zotero file-storage quota. `url` works on every setup including a remote/hosted Zoteus that cannot see your desktop, so prefer it over `path` unless the file really is on the server. Returns the new attachment key.

MIME type; inferred from the extension if omitted (pdf -> application/pdf).

File name to store; inferred from path/url if omitted.

Group library to attach in; forces the cloud path.

Key of the parent item to attach the file to.

Filesystem path to the file, on the machine running Zoteus.

Attachment title, e.g. "Full Text PDF".

URL to download the file from; works on remote/hosted servers.

Zotero attachments (files)

Upload, download, or inspect attachment files. `action`: "upload" stores a file as a Zotero attachment using the full File Storage protocol (provide `url` to have Zoteus fetch it, or `file_path` for a file on the machine running Zoteus; optional `parent_item` to attach it under an item, `title`, `content_type`) and returns the new attachment key; "download" fetches an attachment's file to a local path (provide `item_key`; optional `save_path`, default under the Zoteus data dir) and returns the path and byte count; "info" returns an attachment item's metadata. File bytes are written to / read from disk, never streamed through the conversation. Upload/download use the cloud Web API and your file-storage quota. When Zoteus runs on a different machine than Zotero, `file_path` refers to the server's disk, so use `url` instead.

File to upload, on the machine running Zoteus.

Attachment item key (download/info).

Parent item key to attach under (upload).

Where to write the downloaded file.

URL to download and upload instead of `file_path`; works on remote/hosted servers.

Zotero attachments (files)

Server-rendered bibliography (library items)

Produce a formatted bibliography for items already in a Zotero library, rendered server-side by Zotero in a CSL style. Provide `item_keys` and optionally `style` (name or CSL id; Zotero default is chicago-note-bibliography), `locale`, and `linkwrap`. Returns XHTML. Note: this endpoint is item-only and capped at 150 items. For arbitrary CSL-JSON or items not in the library, use zotero_format_bibliography instead.

Library item keys (max 150).

Wrap URLs/DOIs in links.

Locale (e.g. en-US).

Style name or CSL id.

Create or update Zotero items

Create new items or update existing ones in a single batch (the server auto-chunks into groups of 50). `items` is an ARRAY of item-data objects; each object has `itemType` as a **plain string** (e.g. "journalArticle", "book", "preprint", "report") plus its valid fields, `creators` (each `{creatorType, firstName, lastName}` or `{creatorType, name}`), `tags` (`[{tag}]`), and `collections` (array of 8-char collection keys). To UPDATE an existing item, also include its `key` and current `version`; to CREATE, omit both. Every item is validated against the Zotero schema before anything is sent — if any item is invalid, nothing is written and the problems are returned. Use zotero_schema to discover valid fields/creator types for an itemType. Writes go to the cloud Web API (requires ZOTERO_API_KEY).

Example:
```json
{"items": [{"itemType": "journalArticle", "title": "The Role of Metadata in Machine Learning", "creators": [{"creatorType": "author", "firstName": "Ada", "lastName": "Lovelace"}], "date": "2024-01-15", "DOI": "10.1234/example.5678", "tags": [{"tag": "ml"}], "collections": ["ABCD1234"]}]}
```

Array of Zotero item-data objects (itemType + fields; include key+version to update). Example: {"items":[{"itemType":"journalArticle","title":"The Role of Metadata in Machine Learning","creators":[{"creatorType":"author","firstName":"Ada","lastName":"Lovelace"}],"date":"2024-01-15","DOI":"10.1234/example.5678","tags":[{"tag":"ml"}],"collections":["ABCD1234"]}]}

Array of 8-character collection keys.

Array of creator objects: {creatorType, firstName, lastName} or {creatorType, name}.

The Zotero item type as a plain string, e.g. "journalArticle", "book", "preprint", "report", "thesis".

Object mapping a relation predicate to a URI or array of URIs.

Array of tag objects: [{"tag": "name"}].

Create or update Zotero items

Permanently delete Zotero items

PERMANENTLY and IRREVERSIBLY delete items by key (this purges them — it is NOT the trash). Prefer zotero_trash_items, which is reversible. This tool is disabled unless the server is started with ZOTEUS_ALLOW_DELETE=true, and additionally requires `confirm: true` on every call. For the personal library it goes through the running Zotero desktop app when that app supports local-API writes, otherwise the cloud Web API. The current library version is used as a precondition; the operation auto-chunks to 50 keys per request.

Must be true to proceed with permanent deletion.

Item keys to permanently delete.

Permanently delete Zotero items

Export Zotero items

Export items in a bibliographic format and return the raw text. Choose `format` (bibtex, biblatex, better-biblatex, ris, csljson, csv, mods, tei, coins, rdf_*, refer, wikipedia, bookmarks). `biblatex` is Zotero's STOCK translator via the cloud Web API; BBT-specific options (citation-key generation, sentence-case, biblatexExtendedNameFormat, unicode→LaTeX) are NOT available there. `better-biblatex` uses the local desktop Better BibTeX plugin (your configured BBT export options apply) and is only available when desktop Zotero + BBT are running; it degrades to built-in `biblatex` otherwise. Narrow with `item_keys`, `collection_key`, `q`, or `item_type`. A `limit` (default 50) is always applied. For styled human bibliographies use the bibliography tools.

bibtex biblatex better-biblatex ris csljson csv mods tei coins rdf_bibliontology rdf_dc rdf_zotero refer wikipedia bookmarks

Export Zotero items

Format a bibliography (citeproc / any CSL style)

Render a formatted bibliography in any CSL style using citeproc-js — no Zotero library write required. Provide either `items` (an array of CSL-JSON objects, e.g. from zotero_import or external metadata) or `item_keys` (library items, which are exported to CSL-JSON first). Choose `style` (a name like "APA 7th" or a CSL id; default "apa"), `locale` (default "en-US"), and `format` (html/text/rtf; default html). The formatted bibliography text is returned. Use this for arbitrary items or styles; for items already in the library you can also use zotero_bibliography (server-rendered).

Output format (default html).

Library item keys (exported to CSL-JSON).

CSL-JSON items to format.

Locale (default "en-US").

Style name or CSL id (default "apa").

Attachment full-text

Not a search — to find which items contain a term, use `zotero_search_items` with qmode=everything. This reads, sets, or tracks one attachment's already-extracted full text by key. `action`: "get" returns the indexed text content plus indexing stats for an attachment item (only attachment items have full text; returns found:false if none); "set" stores extracted text for an attachment (provide `content` and the indexing counts); "since" returns the map of attachment keys whose full text changed after a given library `version` (useful for incremental indexing). Only attachment items support full text. "get" and "since" read through the running Zotero desktop app when there is one (no cloud key needed), otherwise the cloud Web API; "set" always writes via the cloud Web API.

Extracted text (set).

Attachment item key (get/set).

Library version for "since" (default 0).

Attachment full-text

Get attachment full text / passages / outline (read-only)

Retrieve an item's PDF or EPUB text for grounding. Pass a parent `item_key` (its best PDF/EPUB attachment is resolved automatically) or an attachment key. With `query`, returns the top relevant passages with locators (char offsets, nearest section, and a page); with `page_range` (e.g. "3-7"), returns just those pages, re-extracted from the PDF so the span is exact; with `outline:true`, returns the PDF's table of contents with page numbers (the cheapest way to decide which pages to read next); with none of them, returns a truncated head. Text comes from Zotero's full-text index when available; when the attachment is NOT indexed yet, the file itself is read and parsed on the fly (`fallback`, on by default; set `fallback:false` to disable), so a PDF added minutes ago still returns text (marked fulltextSource:"pdf" or "epub", with fileSource saying where the bytes came from). The file is read from the running Zotero desktop app, else straight out of the local Zotero storage folder, else downloaded from Zotero cloud storage. Page numbers are exact whenever the PDF was parsed, and otherwise an estimate (pageApprox) unless `precise_pages:true`. Read-only; the indexed text is served by the running Zotero desktop app when there is one, otherwise by the cloud Web API. Use this to cite a claim with a page after finding an item via zotero_search_items / zotero_semantic_search.

When Zotero has no indexed full text for the attachment, read the file itself and extract it directly (default true).

Parent item key or attachment key.

Best-effort cap on total returned text (default 12000); a single passage is never split, so one passage may slightly exceed it.

Max passages (default 5).

Return the PDF's table of contents (heading, page, nesting level) instead of text.

Page span like "3-7" (1-based, inclusive). PDFs only.

Re-extract the PDF for exact page numbers (already the default with `page_range`).

Return top passages relevant to this query.

Get a Zotero item

Fetch one item by its key, returning the full item record (itemType, all bibliographic fields, creators, tags, collections, relations, version). Optionally set `include_children` to also return the item's child notes and attachments. Use `include` to additionally request rendered output: "bib" (formatted bibliography entry), "citation" (inline citation), or "csljson" (CSL-JSON for downstream formatting); combine with `style` (a CSL style id, default chicago-note-bibliography) and `locale`. The returned `version` is required if you later update or delete this item.

Extra rendered content: "bib", "citation", or "csljson".

Also fetch child notes/attachments.

The 8-character Zotero item key.

Locale for bib/citation, e.g. en-US.

CSL style id for bib/citation (default chicago-note-bibliography).

Get a Zotero item

List Zotero groups

List the group libraries the current API key can access, with each group's id, name, type, item count, and edit permissions. Use a returned group id with the `library_id`/`library_type:"group"` parameters of other tools to operate on that group library. Requires a cloud API key.

List Zotero groups

Import items by identifier or URL

Resolve bibliographic metadata to Zotero item-data and optionally save it to your library. `action: "by_identifier"` resolves a DOI, ISBN, PMID, arXiv id, or ADS bibcode (set `identifier`); `action: "by_url"` scrapes a web page (set `url`) and may return multiple choices to pick from. Set `save_to_library:true` (and optionally `collection_key`) to persist the resolved items — saved into the running Zotero desktop app when available, otherwise via the cloud Web API (requires ZOTERO_API_KEY); otherwise the resolved metadata is returned without saving. When a Zotero translation-server is reachable (ZOTEUS_TRANSLATION_SERVER_URL, default http://127.0.0.1:1969) it is the primary path; if none is running, DOI and arXiv ids fall back to built-in resolution (OpenAlex/Crossref and the arXiv API respectively) — the result then carries a `source` field ("scholar" or "arxiv"). ISBN/PMID/bibcode and web URLs require a translation-server.

Title for the attached file, e.g. "Full Text PDF".

File URL (e.g. an arXiv PDF) to download and attach as a stored attachment to the (single) imported item. Works on every save path: the desktop app when one is reachable, otherwise the cloud Web API.

Collection to add saved items to: an 8-char collection key or a Zotero treeViewID like "C20".

DOI (10.…), arXiv id (YYMM.NNNNN), ISBN, PMID, or ADS bibcode.

Persist the resolved items — into the running Zotero desktop app when available, otherwise the cloud Web API (needs a cloud key).

Web page URL to scrape (needs a translation-server).

Import items by identifier or URL

Build the semantic search index

Manage the local hybrid-search index used by zotero_semantic_search. Every job runs in the background on the server, so this tool returns immediately and never blocks on large libraries. THREE write actions, and picking the right one matters: `action: "update"` is the cheap one and should be the default for a library that is already indexed; `action: "build"` and `action: "refresh"` both rebuild the WHOLE index, which on a large library means many minutes and, with an API embedding provider, real spend (they differ in one thing: build resumes an interrupted build, refresh always starts over). `action: "build"`/"refresh" pages the library's top-level items (100-at-a-time, stopping at the server's item cap, ZOTEUS_INDEX_MAX_ITEMS, default 5000, or at a smaller `limit` if one is given), indexes their text (title, abstract, creators, tags) for BM25 keyword search and, if an embedding provider is configured, for vector search, persisting partial progress atomically as it goes; use it for the first build, after changing the embedding model, or to widen a previously capped build. It is ALSO the repair: if the index cannot be read at all, only `action:"build"` clears it, by deleting the unreadable file and opening a fresh one before rebuilding (nothing repairs it at startup or inside a query). `action: "update"` instead fetches only the items changed since the version the index recorded (Zotero's `?since=`), re-chunks and re-embeds just those, and removes items the library no longer holds (diffed from a cheap keys-only `?format=versions` census, since the deletion log is cloud-only); untouched items are never re-embedded, so adding a handful of items costs seconds instead of a full rebuild. Update falls back to a full rebuild by itself, and says so in `updateNotice`, when a delta would be wrong: no version stamp recorded yet, the library is now served by a different Zotero API (the desktop app and the cloud number their versions independently), or the embedding model changed. An update ALSO asks Zotero's full-text index what it has extracted since the build (that is a separate version sequence from item versions, so a PDF Zotero extracted when it was first opened changes no item version and appears in no delta) and indexes the new body text for items nothing else touched; on a library where nothing was extracted, that costs one request. A build or update interrupted by `action:"stop"`, a crash or a restart leaves a checkpoint, and `action: "build"` RESUMES from it: the items already committed stay searchable and are never re-fetched or re-embedded, and only work since the last save is redone (`resumedFrom` on the status reports how many were inherited). `action: "refresh"` is the one that always starts over. A build also indexes the reader's OWN words by default: every child note, and every PDF annotation (its highlighted passage and its comment), as extra passages carrying the parent item's key — so `zotero_annotate` writes text that search can then find, an item with forty annotations still takes one result slot, and a hit whose snippet came from one is marked source:"note" or source:"annotation". That corpus is one paged crawl of hand-written text, orders of magnitude smaller than attachment bodies; turn it off with `own_words:false` or ZOTEUS_INDEX_OWN_WORDS=false. An `action:"update"` keeps it current for the cost of one request when nothing was written: notes and annotations are ordinary items carrying ordinary versions, so an edit, an addition and a deletion are all found by comparing the library's note/annotation keys against the ones the index holds — which is also how an index built before this existed fills its gap, once, on its first update. Set `fulltext:true` to ALSO index the body text Zotero extracted from each item's attachments, which is what makes semantic search match a claim buried in a PDF rather than only its title and abstract; it is off by default because it multiplies build time and index size (default cap: 40000 characters per item, tunable with `fulltext_max_chars`), and only attachments Zotero has already extracted are available. That one pass is REFUSED in one environment: inside Claude Desktop the server runs on Electron's Node, where a build that reaches the full-text pass kills the server process partway through with no error at all (#37), while the identical build completes under standalone Node. The cause is not yet understood, so such a build is refused up front with the way forward rather than attempted, and the index on disk is left exactly as it was: build it once from a terminal against the same ZOTEUS_DATA_DIR and Desktop reads the result. A metadata build (`fulltext:false`) is unaffected there, and so is `action:"update"`, which never enters that pass and keeps a headlessly built index current, body text included. A build runs in TWO passes and reports which one it is on as `phase`: every item's metadata is indexed first, across the whole library, and only then are attachment bodies crawled (`fulltextItemsScanned` of `fulltextItemsTotal`). So the library is fully searchable on titles, abstracts, creators and tags long before a full-text crawl that can run for hours finishes — tell the user they can search already rather than asking them to wait for state:"done". Start a job, then POLL `action: "status"` every few seconds until `state` is "done" (or "error"); calling build or update again while one is running just returns current progress. `action: "status"` reports `state` (idle|building|done|error), `operation` (build|update), `phase` (metadata|fulltext), fetch/embed progress, `itemsRemoved`, index size, the active embedder, `libraryVersion`/`libraryBackend` (the version stamp an update diffs from), `fulltextVersion` (how far into Zotero's separate full-text sequence the index has read), `resumedFrom` (items inherited when a build resumed an interrupted one), `itemsTotal`/`itemsAvailable` (which differ, with a warning, when the cap stopped the crawl short of the library), `ownWordsItems`/`ownWordsPassages` (the notes and annotations indexed, with `ownWordsReason` if they could not be read), and (when full text was requested) `fulltextItems`/`fulltextPassages` plus `fulltextReason` if it produced nothing. It also reports `localApiDegradedAt` when the job saturated Zotero's local API and the whole session fell back to the Zotero Web API: that fallback works, so nothing errors, but the Web API is slower and rate-limited and the rest of the build takes far longer than its start suggested, so tell the user rather than letting them watch an unexplained slowdown (the crawl also backs off to one attachment at a time by itself, to let the app recover). It reports where the index is stored (`storage`: sqlite or memory, set by ZOTEUS_INDEX_BACKEND), `storageNotice` when opening that store imported or refused an older JSON index, `persistError` when the index could not be written to disk at all, and how the last semantic query ranked vectors (`vectorScan`: "codes" for the two-stage path, "exact" for a full scan of every vector, with `vectorScanNotice` when that needs explaining). `action: "stop"` cancels a running job (partial data is kept and stays searchable; a stopped update leaves the version stamp untouched so the next one repeats the delta, and a stopped build leaves a checkpoint the next `action:"build"` resumes from). A partially built index is always usable for keyword search. Local embeddings are CPU-bound (see ZOTEUS_EMBEDDINGS), so large builds take a while: poll status rather than retrying build.

Also index the full text Zotero extracted from each item's attachments, so searches match the body of a PDF. Resource-intensive (slower build, much larger index); defaults to ZOTEUS_INDEX_FULLTEXT (off unless set).

Cap on indexed full-text characters per item; 0 means no cap (default 40000). Only used with fulltext.

Max items to index. Lowers the configured cap for this build only; it cannot raise it. The cap defaults to 5000 and is set by ZOTEUS_INDEX_MAX_ITEMS.

Also index the reader's OWN words — child notes and PDF annotations (highlight text and comments) — as passages carrying the parent item's key. On by default (ZOTEUS_INDEX_OWN_WORDS); the whole corpus is one paged crawl of hand-written text, so it costs a fraction of what fulltext does.

Build the semantic search index

List Zotero collections (read-only)

List collections in a Zotero library (key, name, parent collection key, item count). Read-only — available even in read-only mode (unlike zotero_manage_collections, which also writes). Use the keys to scope zotero_search_items (collectionKey) or zotero_tag_audit (scope.collection_keys).

Only top-level collections.

List Zotero collections (read-only)

List Zotero tags (read-only)

List tags in a Zotero library with their usage count and whether each was auto-applied by Zotero. Optional `q` substring filter and `limit`. Read-only — available even when the connector runs in read-only mode (unlike zotero_manage_tags, which also writes). For taxonomy hygiene use zotero_tag_audit.

Max tags (default 100).

Substring filter.

List Zotero tags (read-only)

Manage Zotero collections

List, create, rename, reparent, or delete collections, and move items into or out of a collection. Set `action` to one of: "list" (all collections with key/name/parent), "create" (needs `name`, optional `parent_collection` key — omit for top-level), "rename" (needs `collection_key` + `name`), "reparent" (needs `collection_key`; `parent_collection` key, or omit to move to top level), "delete" (needs `collection_key`), "add_items" / "remove_items" (need `collection_key` + `item_keys`; collection membership lives on each item). All actions except "list" write to the cloud Web API.

list create rename reparent delete add_items remove_items

Target collection key (all actions except list/create).

Item keys (add_items/remove_items).

Collection name (create/rename).

Parent collection key; omit for top-level.

Manage Zotero collections

Manage Zotero tags

List tags, or add/remove tags on items. Set `action` to "list" (returns library tags; supports `q` substring filter), "add" (add `tags` to each of `item_keys`), or "remove" (remove `tags` from each of `item_keys`). Tags are stored on the parent item's tag array, so add/remove edits the items (cloud Web API). Tag names are case-sensitive.

Items to modify (add/remove).

Substring filter for list.

Tag names to add or remove.

Manage Zotero tags

Manage Zotero saved searches

List, create, or delete saved-search DEFINITIONS. NOTE: the Zotero cloud Web API stores saved searches but does NOT execute them — to get the items a saved search matches, run an equivalent zotero_search_items query (or use the desktop local API when available). Set `action` to "list" (all saved searches with their conditions), "create" (needs `name` and `conditions`, each `{condition, operator, value}`), or "delete" (needs `search_key`). Writes go to the cloud Web API.

Search conditions (create).

Saved-search name (create).

Saved-search key (delete).

Manage Zotero saved searches

Zotero data model (types & fields)

Return the Zotero data model so you never hardcode item shapes. With no arguments, returns the schema version and the list of all item type names. With `item_type`, returns the valid fields and creator types for that type (the "primary" creator type is listed first). Use this to validate an item before creating or updating it: notes, attachments, and annotations are item types too but bypass the normal field/creator model.

If set, return the fields & creator types for this item type.

Zotero data model (types & fields)

Scholarly context (references, citations, related)

Explore the EXTERNAL scholarly graph around a paper (OpenAlex, Crossref fallback). This does NOT search, list, or read your Zotero library — it queries the open web, and results are works from the scholarly web, not your items. To search or inspect YOUR library use zotero_search_items, zotero_semantic_search, zotero_get_item, or zotero_list_tags instead. Provide a `doi` and an `action`: "lookup" (metadata + citation count), "references" (works this paper cites), "citations" (works that cite this paper, most-cited first), or "related" (similar works). Set `include_in_library: true` to additionally flag which results your library already holds (off by default because it scans the library); otherwise every result is just a web record. `limit` caps results (default 20). Read-only; calls external scholarly APIs.

The DOI of the paper (with or without the https://doi.org/ prefix).

Also scan the library and flag results already saved (default false; scanning is expensive).

Max results (default 20).

Search Zotero items

Search or list items in a Zotero library or collection. Quick search via `q` (`qmode`: titleCreatorYear=default, matches title/creator/year only; everything=also searches notes & attachment full text). For presence checks ("is X in my library?"): a default-mode `q` that matches nothing auto-retries once in `everything` mode, so terms appearing only inside PDF text don't false-negative — pin `qmode` explicitly to disable. An empty `everything` result is reported as strong-but-not-conclusive, since un-indexed/scanned/un-synced PDFs aren't full-text searchable. Also supports boolean `itemType` filters (use `||` for OR, repeat or `&&` for AND, leading `-` to negate, e.g. "journalArticle || book", "-attachment"), boolean `tag` filters (same syntax; escape a literal leading hyphen as "\-"), `since` (version) for incremental queries, `sort`/`direction`, and `limit`/`start` paging. Set `response_format` to "detailed" to also return technical fields (version, tags, collections, DOI, url) needed before chaining a write; the default "concise" returns high-signal projections (key, itemType, title, creators, date). Reads are served from the fast desktop local API when available, otherwise the cloud Web API. Returns `totalResults` so you can tell when to page rather than assuming you saw everything. For conceptual/"papers about X" queries by meaning rather than exact fields, use zotero_semantic_search instead.

Restrict to a collection by key.

Boolean itemType filter, e.g. "journalArticle || book".

Max items (default 25, max 100).

Quick/full-text search string.

Detail level of returned items.

Return items modified after this library version.

Boolean tag filter, e.g. "to-read && 2024".

Only top-level items (exclude child notes/attachments).

Search Zotero items

Semantic / hybrid library search

Search the library by meaning, not just keywords. Combines BM25 keyword scoring with vector similarity (when an embedding provider is configured) via reciprocal-rank fusion, and returns the best-matching items with a snippet and score. By default it searches item metadata and abstracts; if the index was built with `fulltext` on (zotero_index fulltext:true, or ZOTEUS_INDEX_FULLTEXT=true) it also searches the body text of attachments, and a hit whose snippet came from a PDF body is marked source:"fulltext". It ALSO searches the words the reader wrote — child notes and PDF annotations (highlight text and comments) — unless that was turned off (ZOTEUS_INDEX_OWN_WORDS=false); a hit from one is marked source:"note" or source:"annotation" and is attributed to the item it hangs off, so an item with forty annotations is one result rather than forty. `mode`: "auto" (hybrid, default), "keyword" (BM25 only), or "semantic" (vector only). "semantic" needs vectors in the index: when the configured embedder is not running (e.g. the on-device model runtime is not installed) it returns an error naming the cause instead of an empty result set, and "auto" keeps working as keyword search while saying so. The index must be built once before first use: when it is empty this tool starts a background build automatically (`auto_build`, on by default) and tells you to poll zotero_index action:"status" and retry — pass `auto_build:false` to opt out. For exact field/tag/itemType filtering use zotero_search_items instead; use this for conceptual/"papers about X" queries. To read the actual passages of a found item (with page locators) use zotero_get_fulltext.

Start building the index automatically in the background when it is empty (default true).

Max results (default 10).

Natural-language query.

Semantic / hybrid library search

Resolve CSL citation styles

Resolve a human citation-style name to a valid CSL style id and confirm it is available, or list common style aliases. `action: "resolve"` maps names like "APA 7th", "IEEE", "Vancouver", "Chicago", "MLA", "Nature" to the correct CSL id (e.g. apa, ieee, modern-language-association) and verifies the style can be fetched; pass the returned `styleId` as the `style` argument to zotero_format_bibliography or zotero_bibliography. `action: "list"` returns the built-in common aliases (any id from the CSL styles repository also works). Dependent styles are resolved to their independent parent automatically when formatting.

Style name to resolve (e.g. "APA 7th").

Resolve CSL citation styles

Incremental sync delta

Return what changed in a library since a given version, for efficient incremental sync. Provide `since` (a library version; 0 = everything). Returns, per object type (items/collections/searches/tags), the map of keys→version that changed after `since`, plus the deletion log (keys removed since `since`). This is the version-based delta the Zotero sync algorithm uses — fetch the changed keys, then pull only those with zotero_get_item/zotero_search_items. Reads via the cloud Web API.

Include the deletion log (default true).

Library version to diff from (default 0).

Which object types to check (default all).

Incremental sync delta

Audit tags against a controlled vocabulary

Audit a library against a controlled tag vocabulary with priority tiers. Provide the vocabulary inline as `vocabulary` (or a JSON file via `vocabulary_path`): { tags:[{name,tier?}], tiers?:[{name,required?}] }. Reports (1) off-taxonomy tags (library tags not in the vocabulary; Zotero auto-applied tags are bucketed separately unless include_auto), (2) items missing a tag from each required tier, and (3) optional per-collection coverage when `scope.collection_keys` is given. Read-only. Tag/auto-tag enumeration uses the cloud Web API.

Treat Zotero auto-applied tags as off-taxonomy too.

Max items listed per report (default 50).

Path to a JSON file with the vocabulary.

Trash or restore Zotero items

Move items to the trash (the safe, REVERSIBLE default) or restore them. This sets the `deleted` flag (1=trash, 0=restore) — it is NOT a permanent delete, so trashed items can be recovered here or in the Zotero app. Use this instead of zotero_delete_items unless you truly need irreversible removal. Provide `item_keys` and optional `action` (default "trash"). Writes go to the running Zotero desktop app for your personal library (via its local-API writes where available), otherwise to the cloud Web API.

Default "trash".

Item keys to trash or restore.

Trash or restore Zotero items

Update a Zotero item

Partially update one item (HTTP PATCH — only the fields you supply change; omitted fields are preserved). Provide `item_key` and a `patch` object of the fields to change (e.g. {"title":"New","extra":"note"} or {"tags":[{"tag":"reviewed"}]}). All field values are plain JSON strings/numbers/booleans/arrays — never wrapped in nested objects (e.g. `"title": "New"`, NOT `"title": {"title": "New"}`). Optimistic concurrency is handled for you: if you pass the item's `version` it is used; otherwise the current version is fetched first. If the item changed on the server in the meantime (412), the update is automatically re-fetched and retried once. Writes go to the cloud Web API. Set `dry_run:true` to preview the field-level before→after diff without writing (arrays like tags/collections are replaced wholesale by PATCH, not merged; a dry_run call performs no write).

Preview the field-level before→after diff without writing.

The 8-character item key.

Object of fields to change (PATCH semantics), e.g. {"title": "New title", "date": "2024-02-01", "tags": [{"tag": "reviewed"}], "collections": ["ABCD1234"]}. Structured fields (creators, tags, collections, relations) must be real JSON arrays/objects, not JSON-encoded strings. Values are plain, never wrapped in nested objects.

Array of 8-character collection keys.

Object mapping a relation predicate to a URI or array of URIs.

Array of tag objects: [{"tag": "name"}].

Known current version; fetched automatically if omitted.

Update a Zotero item

Zotero identity & access

Resolve the current Zotero identity (userID, username, display name) and per-library access scopes from the configured API key, and report which library backends are available (cloud Web API and/or the desktop local API). Call this first to discover the userID — never ask the user to type a numeric ID. If no API key is configured, the server runs in local-only read mode against the desktop library (users/0).

Zotero identity & access