Skip to content

Toolset gating ​

A large flat tool surface degrades agent tool-selection and burns context. So the live surface stays small: core + inspection are exposed by default (24 tools), and 27 further toolsets are gated off until the agent enables them.

The enabled set ​

enable_toolset / disable_toolset write into a single server-global enabled set (issue #364). godot-mcp is a single-user, locally-run server — per-session isolation was removed because it was dead code on the sessionless MCP 2026-07-28 protocol (a fresh session_id per call made it useless) and violated the no-server-initiated-hook rule. Consequences, which are features:

  • A grant issued on one connection persists to a fresh connection against the same server process — a reconnect between enable and mutate keeps the grant.
  • All clients see the same surface. There is no per-session isolation to reason about.
  • The set is seeded at startup from GODOT_MCP_DEFAULT_TOOLSETS (all or a comma-separated list; unknown names are logged and ignored). The agent-facing protocol text derives its wording from the effective default so it never claims "everything is hidden" when the operator seeded more (#425).

tools/list_changed ​

Since 2026.09.17 (#491/#485), enable_toolset/disable_toolset emit notifications/tools/list_changed after mutating the set. Clients that cache the tool list at session start refresh on the notification. The send is fire-and-forget by design — the toggle already succeeded, so a delivery failure must not fail the tool call — but it is logged, never swallowed silently.

On the sessionless 2026-07-28 protocol the spec forbids unsolicited change notifications: they reach a client only on a subscriptions/listen stream it opens. Since #620 the server registers a ListenHandler (backed by an in-memory SubscriptionBus, the same wiring mcp's own MCPServer uses) on FastMCP's low-level server, so it both advertises tools.listChanged: true in server/discover and fans each toggle out to every open listen stream. Registering the method is what flips the advertised flag — the mcp SDK derives it from whether subscriptions/listen is served, not from FastMCP's legacy notification options.

Pull-based fallback (#621). Because many clients still ignore the notification, enable_toolset's result also carries the enabled tools' exposed names, descriptions, and input schemas — so an agent can call them immediately even when its registry never refreshes. The push that keeps the editor dock's Toolsets row honest is fire-and-forget (#624) and never stalls the toggle.

The typed tools remain authoritative either way: calling a gated tool whose toolset is disabled fails with a structured gating error, and _ensure_or_return_error-style client helpers re-enable idempotently.

Why the gate is worth its friction ​

  1. Tool selection sharpness. With 194 tools exposed, models confuse similar tools and waste calls. The default 24-tool surface fits in context with room to reason.
  2. Safety surface minimization. Destructive tools (delete_node, reload_scene, close_scene, bus removers, file deleters) are invisible until the agent deliberately asks for a toolset containing them.
  3. Version gating rides the same flag: scene_edit, input_map, tilemap, scene_3d require Godot 4.4+ and refuse to enable on older editors with a structured error naming the requirement.

The toolsets ​

DomainToolsets
Scene buildingscene_edit · composite (macro round-trips) · scene_3d · theme_ui · tilemap · particles · navigation · physics · animation · audio · shader · visual_shader
Code & datascripts · resources_edit · project · project_scaffold
Run & verifyruntime · input · testing · debugger · profiling
Bulk & shipbatch · analysis · export · asset_import · editor

Plus always-on core (diagnostics, toolset management, godot_undo, godot_debug_workflow) and inspection.

The full per-toolset table (with tool names and counts) lives in the toolsets reference; the authoritative per-tool spec is docs/tool-contracts.md.

Design guardrails ​

  • New categories register gated off — added to TOOLSETS, never to DEFAULT_ENABLED. Growth of the catalog does not grow the default surface.
  • Fewer, richer tools over many micro-tools. Create-with-config (create_node(…, properties?, script?)) and batch setters replace one-tool-per-field. One tool per noun with a few clear verbs.
  • Never merge across safety classes. Folding delete into a fat node tool would bury the confirm gate; the class split is deliberate.
  • Never a single do(action, params) dispatcher — that discards the per-tool schemas and descriptions that guide the agent.

194 tools · 29 toolsets · contract version 1