Godot-MCP
Provides GitHub Copilot with tools to create and edit Godot scenes, nodes, and scripts directly within the editor.
Enables Google's AI models (such as Gemini) to drive Godot Editor operations, including scene editing, resource management, and script generation.
Lets OpenAI models (e.g., GPT-4, Codex) interact with the Godot Editor to build scenes, manage assets, and execute custom code via reflection.
Godot MCP is an AI-powered game development assistant for the Godot Editor. Connect Claude, Cursor, Copilot, or any MCP-aware agent to Godot and let it inspect and drive your project β create nodes, edit scenes, manage resources and scripts, capture screenshots, and more.
Godot-MCP is the Godot counterpart of Unity-MCP: a C# editor addon that exposes Godot Editor operations as AI Tools and connects them to an MCP server through the same hosted cloud backend (ai-game.dev) that powers Unity-MCP β or your own self-hosted server. The MCP / reflection stack is not forked: it is shared with Unity-MCP and consumed from nuget.org as PackageReferences.
π¬ Join our Discord Server β Ask questions, showcase your work, and connect with other developers!
βοΈ AI agents β Use the best agents from Anthropic, OpenAI, Google, or any other provider with no vendor lock-in
βοΈ 39 built-in Tools β A wide range of MCP Tools across 11 families for operating the Godot Editor
βοΈ C# & GDScript β Read, create, and update both
.csand.gdscripts, and attach them to nodesβοΈ Scene & Node control β Build and edit the scene tree, open/save
.tscnscenes, mutate.tres/.resresourcesβοΈ Visual feedback β Capture viewport, camera, and isolated-node screenshots the LLM can inspect
βοΈ Reflection escape hatch β Find and call any C# method across loaded assemblies via ReflectorNet
βοΈ Cloud or self-hosted β Connect to
ai-game.devout of the box, or point at your own serverβοΈ Natural conversation β Chat with AI like you would with a human
Quick Start
Get up and running from a terminal using the godot-cli (the Godot analog of unity-mcp-cli) β no manual file copying or csproj editing required:
# 1. Install godot-cli
npm install -g godot-cli
# 2. (Optional) Scaffold a fresh Godot C# project β skip if you already have one
godot-cli create-project --dotnet ./MyGodotProject
# 3. Install the godot_mcp addon: downloads addons/godot_mcp/ from the matching
# GitHub release, adds the required NuGet packages + the extension-catalog
# EmbeddedResource to your .csproj, and enables the plugin in project.godot β
# all idempotently
godot-cli install-plugin ./MyGodotProject
# 4. Pick an AI agent (Claude Code, Cursor, Copilot, β¦) and write its MCP config
godot-cli setup-mcp claude-code ./MyGodotProject
# 5. Open the Godot editor β builds the C# assembly first (so the addon loads on
# the very first open) then auto-connects with the right GODOT_MCP_* env vars
godot-cli open ./MyGodotProject
# 6. Wait until the plugin answers the readiness probe
godot-cli wait-for-ready ./MyGodotProjectThat's it. Ask your AI "Create 3 cubes in a circle with radius 2" and watch it happen. β¨
Offline / dev install:
install-plugin --source <path-to>/addons/godot_mcpcopies the addon from a local directory instead of downloading it. Prefer the matching release version withinstall-plugin --version <x.y.z>if you need a specific addon build. The manual route (copy the addon + add the NuGet packages yourself) is still documented under Installation Steps 1β2 for the Asset Library / hand-managed flows.
See the full CLI documentation for every command, editor-resolution order, and connection env vars.
Contents
Tools Reference
Godot-MCP ships 39 built-in tools grouped into 11 families. Tool names mirror Unity-MCP where
sensible (scene-*, node-*, β¦). Every tool returns a structured, ReflectorNet-serialized
result (or a PNG image for screenshots). All editor tools are available immediately after the addon is
enabled β no extra configuration required. The runtime-errors family is the exception: it surfaces
errors from the running game and is OFF by default β opt in with builder.WithRuntimeErrorCapture()
(see Capturing in-game runtime errors).
Family | Tools | What it does |
ping |
| Lightweight readiness probe β echoes a message back, or returns |
node |
| Inspect and edit the active scene tree (the Godot analog of Unity GameObjects), driving |
scene |
| Open, save, create, and inspect Godot scenes ( |
resource |
| Find and mutate Godot resources ( |
filesystem |
| Browse and reimport the project's |
script |
| CRUD on C# ( |
screenshot |
| Capture the editor viewport, a specific camera, or an isolated node render, returned as a PNG image the LLM can inspect. |
editor |
| Read/drive the editor run-and-play lifecycle (Godot launches the game in a separate process) and the current selection. |
console |
| Read and clear the plugin's editor log collector ( |
reflection |
| Find and call C# methods (static/instance, public/private) across every loaded assembly via ReflectorNet β the engine-agnostic escape hatch. |
runtime-errors |
| Poll errors raised inside the running game (NOT the editor) β GDScript runtime errors, |
ping
pingβ Lightweight readiness probe; echoes a message back, or returnspong.
node
node-findβ Find nodes in the active scene tree by path, type, or name.node-createβ Create a new node under a parent (optionally instancing a.tscnsub-scene).node-modifyβ Set fields/properties on one or more nodes.node-set-parentβ Reparent nodes within the scene tree.node-duplicateβ Duplicate nodes together with their subtrees.node-deleteβ Delete nodes from the active scene.
scene
scene-openβ Open ares://*.tscnPackedScene in the editor.scene-saveβ Save an open scene back to its.tscnfile.scene-createβ Create a new scene asset in the project.scene-list-openedβ List the scenes currently open in the editor.scene-get-dataβ Retrieve the root nodes / structure of a scene.
resource
resource-findβ Search the project for resources (.tres/.res).resource-get-dataβ Read a resource's serialized fields and properties.resource-modifyβ Modify a resource's properties.resource-createβ Create a new resource asset.resource-moveβ Move / rename a resource, keeping.importsidecars consistent.resource-deleteβ Delete a resource from the project.
filesystem
filesystem-listβ Browse theres://tree (file types + uids) via the editor file index.filesystem-reimportβ Reimport files in the project.
script
script-readβ Read a.cs/.gdscript file.script-createβ Create a new script file.script-updateβ Update an existing script file's contents.script-deleteβ Delete a script file.script-attach-to-nodeβ Attach a script to a node.script-validateβ Validate GDScript (.gd) files and return structured parse/compile diagnostics.
screenshot
screenshot-viewportβ Capture the editor viewport as a PNG.screenshot-cameraβ Capture from a specific camera.screenshot-isolatedβ Render a node in isolation from a chosen angle.
editor
editor-application-get-stateβ Read the editor application/run state.editor-application-set-stateβ Start / stop the running game.editor-selection-getβ Get the current editor selection.editor-selection-setβ Set the current editor selection.
console
console-get-logsβ Read the plugin's collected editor logs (with filtering). This includes the plugin's connection lifecycle diagnostics (connect/disconnect, drain-timeout, config save/load, skill-gen, dev-control, dispatcher, and runtime-capture warnings), which route through the same capture sink as its framework logs.console-clear-logsβ Clear the collected log cache.
reflection
reflection-method-findβ Find C# methods (including private) across every loaded assembly.reflection-method-callβ Call any C# method with input parameters and get the result.
runtime-errors (in-game; OFF by default β enable with builder.WithRuntimeErrorCapture())
runtime-errors-getβ Read captured in-game runtime errors (oldest-first, newest-kept page); poll only new errors viasinceSequence. Returnsavailable:falsewhen capture was never enabled, so an empty list is never mistaken for health.runtime-errors-clearβ Clear the captured in-game runtime-error buffer (a no-op when capture is not enabled); the monotonic sequence counter is preserved.
Requirements
Godot 4.3+ β the C# / .NET (mono) edition. The addon csproj pins
Godot.NET.Sdk/4.3.0as its minimum floor; newer 4.x editors (4.4, 4.5) work..NET 8 SDK (
net8.0).
Godot-MCP requires themono (C#/.NET) build of Godot β the standard (GDScript-only) build cannot compile the addon.
Installation
There are two things to install: the addon (the plugin files) and the two NuGet packages the
addon's C# depends on. Godot compiles every .cs under your project into one assembly, so your
project's .csproj must declare the same NuGet references the addon needs β otherwise the addon's C# will
not compile.
Related MCP server: GodotIQ
Step 1: Add the addon
Pick one of the following ways to get the addons/godot_mcp/ folder into your Godot C# project.
Fully automated (recommended for terminal workflows):
godot-cliinstall-plugin ./MyGodotProjectdoes all of Step 1 and Step 2 in one command β it downloadsaddons/godot_mcp/from the matching GitHub release, adds the two NuGet packages and the extension-catalog<EmbeddedResource>to your.csproj, and enables the plugin inproject.godot, idempotently. Use--source <path>/addons/godot_mcpto install from a local copy offline. The manual Options AβC below remain for in-editor (Asset Library) and hand-managed installs.
Option A β Godot Asset Library (recommended)
The easiest path: install directly from inside the editor.
Open the AssetLib tab at the top of the Godot editor.
Search for Godot-MCP and open the asset.
Click Download, then Install β Godot unpacks the addon into your project's
res://addons/godot_mcp/.
The Asset Library entry is published per release and always points at a tagged version, so an in-editor install gives you a known-good snapshot of the addon. (See note below if the entry is not visible yet.)
Option B β GitHub Release zip
Grab the latest godot-mcp-addon-<version>.zip from the
Releases page and extract it into your
project's root β the archive already contains addons/godot_mcp/..., so the files land at
res://addons/godot_mcp/.
Option C β copy from source
Copy the addons/godot_mcp/ folder from this repository (or your clone) into your project's addons/
directory by hand.
After the files are in place (Options AβC), enable the plugin:
Project β Project Settings β Plugins β Godot-MCP β Enable. (If you used the fully-automated
godot-cli install-plugin above, the plugin is already
enabled and the NuGet packages + extension-catalog embed are already added β skip straight to
Step 3.)
On a successful load the editor Output panel prints:
[Godot-MCP] plugin loadedAsset Library availability. The in-editor AssetLib entry (Option A) appears after the maintainer's first submission is approved by the Godot Asset Library moderators. Until then, use Option B (GitHub Release zip) or Option C.
Step 2: Add the NuGet packages + the extension catalog embed
Add both PackageReferences and the extension-catalog <EmbeddedResource> to your project's
.csproj (use these exact pinned versions β they must match the addon's Godot-MCP.csproj):
<ItemGroup>
<PackageReference Include="com.IvanMurzak.ReflectorNet" Version="5.3.1" />
<PackageReference Include="com.IvanMurzak.McpPlugin" Version="6.10.0" />
</ItemGroup>
<!-- Embed the extension catalog so the Extensions panel populates (else it is EMPTY). -->
<ItemGroup>
<EmbeddedResource Include="addons/godot_mcp/extensions.catalog.json" LogicalName="Godot-MCP.extensions.catalog.json" />
</ItemGroup>Package | Version | Role |
| Reflection / serialization core | |
| MCP plugin client (transitively pulls |
The <EmbeddedResource> is as required as the NuGet pins: the addon's pure-managed extension
registry reads the catalog at editor runtime via GetManifestResourceStream (no res:// / filesystem
fallback), and because the addon ships as source its own <EmbeddedResource> does not carry into your
project β so without this line your Extensions panel is empty. The LogicalName must be exactly
Godot-MCP.extensions.catalog.json so the resource resolves identically to the addon's own assembly.
Run dotnet restore so the packages land in your NuGet cache, then build. No manual DLL copying is
required β at editor runtime the addon's assembly resolver locates the DLLs in your NuGet
global-packages folder by reading the build's *.deps.json. (If you prefer self-contained output, set
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> so the DLLs are copied beside your
project assembly instead.)
Step 3: Install an AI agent
Choose a single AI agent you prefer β you don't need to install all of them. This is your main chat
window to communicate with the LLM.
Claude Code (recommended)
Any other MCP-aware agent
Write the agent's MCP-client config with godot-cli setup-mcp <agent> ./MyGodotProject β it points the
client at the Godot server's <host>/mcp URL. See the
CLI documentation for the full list of
supported agents.
Connect
The plugin connects to an MCP server in one of two modes. The mode and its URL / token can be set in the
serialized config or overridden at process start with environment variables (handy for CI, headless runs,
and local dev). All variable names are the Godot analog of Unity-MCP's UNITY_MCP_*. The active mode
always recomputes from the environment, so a process-level override wins over the serialized config
without editing any file.
Cloud mode (default) β ai-game.dev
In Cloud mode the plugin connects to the hosted backend at https://ai-game.dev (the /mcp hub path
is appended automatically). This is the default connectionMode.
Environment variable | Purpose | Default |
| Force the mode: |
|
| Override the cloud base URL. A trailing |
|
| Bearer token, routed to the active mode's token. Surrounding quotes are trimmed. | (none) |
Custom mode β your own server
In Custom mode the plugin connects to a server URL you supply (a local dev server, a self-hosted instance, etc.).
Environment variable | Purpose | Default |
| Set to |
|
| The custom server URL. Must be an absolute http(s) URL or it falls back to the default. |
|
| Bearer token (only needed if the server requires authorization). | (none) |
Example β boot the editor pointed at a local server:
export GODOT_MCP_CONNECTION_MODE=Custom
export GODOT_MCP_HOST=http://localhost:5300
# export GODOT_MCP_TOKEN=... # only if the server enforces authThe
godot-cli opencommand forwards these env vars for you via--mode,--url,--cloud-url, and--tokenflags.
Godot MCP Server setup
In Cloud mode you don't run a server at all β the plugin talks to ai-game.dev. If you want to host
the server yourself (local dev, CI, or your own cloud), you have two options: let the addon download and
run the matched server binary for you (recommended), or run it manually (advanced).
The server itself is the shared, engine-agnostic
GameDev-MCP-Server β one server binary
(gamedev-mcp-server) serving Unity-MCP, Godot-MCP, and Unreal-MCP. It is released from its own repo on
its own version line; this addon pins the server version it consumes (the ServerVersion constant in
addons/godot_mcp/Runtime/Connection/GodotMcpServerView.cs).
Local server β let the addon download & run it for you
In Custom mode the plugin can host its own MCP server β you don't have to build or launch anything by hand. Open the addon dock's Server card while Custom mode is selected and use the Local server row:
Start Server β downloads the server build for the pinned server version, caches it, launches it, and the plugin connects to it. Stop Server terminates it (it is also stopped automatically when you close the editor).
The download is the per-platform release asset
gamedev-mcp-server-<rid>.zipβ pulled over HTTPS fromgithub.comonly, from the GameDev-MCP-Server release taggedv<ServerVersion>, so the asset URL is:https://github.com/IvanMurzak/GameDev-MCP-Server/releases/download/v<ServerVersion>/gamedev-mcp-server-<rid>.zip. The<rid>(platform runtime identifier β e.g.win-x64,osx-arm64,linux-x64) is resolved automatically for your machine; all seven published RIDs are supported (win-x64/x86/arm64,linux-x64/arm64,osx-x64/arm64).The binary is cached under your project's
.godot/mcp-server/<rid>/folder (gitignored) and re-used on later launches; it is only re-downloaded when the pinned server version changes (an exact version match, so the editor plugin and the server it talks to never drift). The server is launched on the port from your Server URL (defaulthttp://localhost:8080), over thestreamableHttptransport.
Version pinning & security. The download URL is derived solely from the addon's pinned
ServerVersionconstant and your platform RID β there is no arbitrary-URL binary execution. The addon version and the server version are decoupled: bumping the consumed server is an explicit addon change (a newServerVersion), and the pinnedv<ServerVersion>release must already exist on GameDev-MCP-Server before an addon release that pins it. If the release asset can't be fetched (you're offline), the addon logs a warning and the local server simply doesn't start β fall back to the manual run below, or use Cloud mode. The download is skipped entirely under CI (theCI/GITHUB_ACTIONSenvironment), where no local server is hosted.
This mirrors Unity-MCP's self-hosted server flow: the editor plugin manages the pinned server binary for you instead of requiring a manual build.
Run the server manually (advanced)
To run the server as a standalone / cloud process, download a
GameDev-MCP-Server release binary (or use the
aigamedeveloper/mcp-server Docker image). Both
transports are supported: streamableHttp (HTTP) and stdio.
# HTTP transport on port 8080
./gamedev-mcp-server --client-transport streamableHttp --port 8080
# stdio transport β for local MCP clients that launch the server directly
./gamedev-mcp-server --client-transport stdioThen point the plugin at it in Custom mode
(GODOT_MCP_HOST=http://localhost:8080).
Choosing a transport: use
stdiowhen the MCP client launches the server binary directly (local use β the most common setup); usestreamableHttpwhen running the server as a standalone process or in the cloud and connecting over HTTP.
See the GameDev-MCP-Server README for the full argument / environment-variable table, the Docker image, and the cross-platform build matrix.
Customize Tools
Godot-MCP supports custom MCP Tool development directly in your project code. A tool family is a
partial class decorated [AiToolType]; each tool method is decorated [AiTool("tool-name", β¦)] with a
[Description] on the method and on each parameter to help the LLM understand it.
Any Godot API call (
Node,Resource,EditorInterface, β¦) must run on the editor main thread β marshal it throughMainThread.Instance.Run(...)(ReflectorNet'sMainThreadis backed by the Godot main-thread dispatcher on plugin boot). Never touch engine objects off-thread.
[AiToolType]
public partial class Tool_MyFeature
{
[AiTool("my-custom-task", Title = "Do a custom task")]
[Description("Explain to the LLM what this does and when to call it.")]
public string CustomTask
(
[Description("Explain to the LLM what this parameter is.")]
string inputData
)
{
// ... work that does not touch the Godot API can run on this background thread ...
return MainThread.Instance.Run(() =>
{
// ... touch EditorInterface / Node / Resource here, on the main thread ...
return "[Success] Operation completed.";
});
}
}Return a structured data model (ReflectorNet-serialized) or void for side-effect-only ops β never ad-hoc
string formatting for parseable output. Use string? optional = null parameters (nullable + default) to
mark them as optional for the LLM.
Runtime usage (in-game)
Everything above runs the MCP connection inside the Godot editor (the [Tool] EditorPlugin boots
it for you). Godot-MCP can also run inside a running / exported game build (debug or release) β
the Godot analog of Unity-MCP's runtime mode. This lets an LLM read and drive your live game state:
imagine a Chess game whose bot logic you outsource to an LLM by exposing a couple of tools.
Two things make runtime mode different from editor mode, and both are deliberate:
It never auto-connects. The editor plugin connects on boot; a game build does not. You write the opt-in code and decide when (if ever) to call
Connect().There are no tools, prompts, or resources by default β strictly manual. The runtime ships zero MCP tools, prompts, and resources. You register every
[AiToolType]tool,[AiPromptType]prompt, and[AiResourceType]resource yourself, in your own code β and each kind is independently optional (register prompts without any tools, or vice versa). (The addon's editor tool families are gated by#if TOOLSand don't even compile into a game build, so they can never leak in.)
The entry point is GodotMcpRuntime.Initialize(...) (namespace com.IvanMurzak.Godot.MCP.Runtime).
Write it once β e.g. from a Godot autoload's _Ready() so a SceneTree exists:
using System.Reflection;
using com.IvanMurzak.Godot.MCP.Connection; // GodotMcpConnectionMode, GodotMcpAuthOption
using com.IvanMurzak.Godot.MCP.Runtime; // GodotMcpRuntime
using Godot;
public partial class GameMcp : Node
{
private GodotMcpRuntimeHandle? _mcp;
public override async void _Ready()
{
// 1) Build the connection (default OFF β nothing connects yet).
_mcp = GodotMcpRuntime.Initialize(builder =>
{
builder.WithConfig(config =>
{
config.ConnectionMode = GodotMcpConnectionMode.Custom; // your own server
config.Host = "http://localhost:8080"; // prefer loopback
config.AuthOption = GodotMcpAuthOption.Required; // require a bearer token
config.Token = "your-secret-token";
});
// 2) Opt YOUR tools / prompts / resources in. Zero of each by default β this is the only way
// they get registered, and each kind is independently optional.
builder.WithToolsFromAssembly(Assembly.GetExecutingAssembly()); // [AiToolType] classes
builder.WithPromptsFromAssembly(Assembly.GetExecutingAssembly()); // [AiPromptType] classes
builder.WithResourcesFromAssembly(Assembly.GetExecutingAssembly()); // [AiResourceType] classes
// β¦or register specific families:
// builder.WithTools(typeof(GameMcpTools));
// builder.WithPrompts(typeof(GameMcpPrompts));
// builder.WithResources(typeof(GameMcpResources));
}).Build();
// 3) Connect β explicit, the security-required opt-in. Retries in the background while
// KeepConnected is true (the default).
await _mcp.Connect();
}
public override async void _ExitTree()
{
// 4) Disconnect on shutdown (or whenever you want to stop exposing tools).
if (_mcp is not null)
await _mcp.Disconnect();
}
}Builder surface (all fluent / chainable):
Call | What it does |
| Begin configuring; returns a |
| Set |
| Register every |
| Register specific |
| Register every |
| Register specific |
| Register every |
| Register specific |
| Skip the automatic main-thread-dispatcher bootstrap (only if you install your own autoload dispatcher). |
| Capture errors raised in the running game (GDScript runtime errors, |
| Finalize; returns a default-OFF |
| Open / close the connection. |
Initialize().Build()also guarantees a main-thread dispatcherNodein the runningSceneTree(so tool handlers can marshal Godot API calls onto the engine main thread), unless you opt out withWithoutMainThreadDispatcher(). Call it once aSceneTreeis live (e.g. from an autoload_Ready).
Capturing in-game runtime errors
In editor mode, console-get-logs and script-validate surface the plugin's own logs and GDScript
parse errors. But errors raised inside a running game β a GDScript runtime error (a null
dereference, a bad index), a push_error/push_warning, a shader error, or a C# unhandled exception β
are not visible to an agent through those editor tools. Without this, an agent can launch the game, poll
for logs, see silence, and wrongly conclude the game is healthy. This is the gap that blocks an unattended
"keep fixing until no errors" loop for real gameplay/runtime bugs.
Opt in with WithRuntimeErrorCapture():
_mcp = GodotMcpRuntime.Initialize(builder =>
{
builder.WithConfig(cfg => { /* host / token β¦ */ });
builder.WithRuntimeErrorCapture(); // capture in-game runtime errors + expose the runtime-errors-* tool
}).Build();
await _mcp.Connect();That single call installs three best-effort capture channels and registers the runtime-errors-* tool:
Engine error stream (Godot 4.5+) β registers a
Godot.LoggerviaOS.AddLogger, so GDScript runtime errors,push_error/push_warning, and shader errors raised in the running game are captured with their origin (file/line/function).C# unhandled exceptions β
AppDomain.CurrentDomain.UnhandledException, with the full managed stack trace.C# unobserved
Taskexceptions βTaskScheduler.UnobservedTaskException, with the full managed stack trace. (It only observes for logging β it does not callSetObserved(), so your game's own escalation behavior is unchanged.)
An MCP client polls the captured errors with the runtime-errors-get tool (and clears the buffer with
runtime-errors-clear):
Tool | What it returns / does |
| A bounded, newest-kept list of |
| Clears the captured buffer (the monotonic |
Stack-trace fidelity (read this). The two error sources differ in depth:
Engine errors (
source: Engine) carry the error's origin βfile:lineand the originatingfunctionβ plus the message and atype(Error/Warning/Script/Shader). On Godot 4.5+, a GDScript runtime error also carries the deep multi-frame call stack:framesis the ordered (innermost-first) backtrace β each{ function, file, line }β andstackTraceis the engine's formatted rendering of it. On Godot < 4.5 (or a release build without call-stack tracking)framesisnullandstackTraceisnull(origin only). The frames are materialized off the engine's non-thread-safeScriptBacktraceinside the logger callback on the originating thread β only plain managed values ever cross into the collector, never a live engine object.C# faults (
source: UnhandledException/UnobservedTaskException) carry the full managed stack trace (inner exceptions inlined) instackTrace, plus the CLR exception type name intype. (framesisnullβ the managed stack lives in thestackTracestring.)
Graceful degradation. On Godot < 4.5 there is no
OS.AddLoggermanaged hook, so the engine channel is silently unavailable β the C# exception channels still work, andruntime-errors-getstill functions (it just won't see GDScript runtime errors). And like the rest of runtime mode, capture is strictly opt-in β withoutWithRuntimeErrorCapture()nothing is hooked and there is no behavior change. Disposing the handle (handle.Dispose()) uninstalls the hooks.
β οΈ Security β information disclosure. Captured errors forward the full message and (for C# faults) the full managed stack trace to the connected agent through
runtime-errors-get. Those strings can embed sensitive runtime data β absolute filesystem paths, machine/user names, query strings, or a secret/token that happened to appear in an exception message or argument. That is the intended diagnostic value, but it widens what is exposed over the connection. EnableWithRuntimeErrorCapture()only on a trusted connection: a loopback host (http://localhost:β¦/127.0.0.1) withAuthOption = GodotMcpAuthOption.Requiredand a real token β never an unauthenticated public interface in a release build. See Security anddocs/runtime-security.md.
Sample: a live game-state tool
A runtime tool is written exactly like an editor tool β a partial class decorated [AiToolType], each
method decorated [AiTool("tool-name", β¦)] with a [Description] on the method and each parameter. Any
Godot API call (Node, SceneTree, β¦) must run on the engine main thread β marshal it through
MainThread.Instance.Run(...) (ReflectorNet's MainThread, backed by the dispatcher Initialize()
bootstrapped for you).
This Godot analog of Unity-MCP's "Chess bot" sample exposes the live running SceneTree to the LLM β
a pure-managed game-ping round-trip plus a game-scene-tree-summary that reads real Node state:
using System.ComponentModel;
using com.IvanMurzak.McpPlugin; // [AiToolType], [AiTool]
using com.IvanMurzak.ReflectorNet.Utils; // MainThread
using Godot;
[AiToolType]
public partial class GameMcpTools
{
[AiTool("game-ping", Title = "Game Ping", ReadOnlyHint = true, IdempotentHint = true)]
[Description("Runtime readiness probe. Echoes 'message' back, or returns 'pong-from-game' when omitted.")]
public string GamePing(
[Description("Optional message to echo back. When null/empty, returns 'pong-from-game'.")]
string? message = null)
{
return string.IsNullOrEmpty(message) ? "pong-from-game" : message;
}
[AiTool("game-scene-tree-summary", Title = "Game Scene-Tree Summary", ReadOnlyHint = true)]
[Description("Summary of the LIVE running game's SceneTree (current scene + root child node names).")]
public SceneTreeSummary GameSceneTreeSummary()
{
// Touch the live SceneTree on the engine main thread β MainThread.Instance was installed by
// GodotMcpRuntime.Initialize(...). Touching Node APIs off the main thread would fault.
return MainThread.Instance.Run(() =>
{
var summary = new SceneTreeSummary();
if (Engine.GetMainLoop() is not SceneTree tree || tree.Root == null)
{
summary.CurrentSceneName = "<no-scene-tree>";
return summary;
}
summary.CurrentSceneName = tree.CurrentScene?.Name ?? "<none>";
summary.RootChildCount = tree.Root.GetChildCount();
foreach (var child in tree.Root.GetChildren())
summary.RootChildNames.Add(child.Name);
return summary;
});
}
}
// Structured result (ReflectorNet-serialized β never ad-hoc string formatting for parseable output).
public sealed class SceneTreeSummary
{
public string CurrentSceneName { get; set; } = string.Empty;
public int RootChildCount { get; set; }
public System.Collections.Generic.List<string> RootChildNames { get; set; } = new();
}Register it from the Initialize(...) block above (WithToolsFromAssembly(Assembly.GetExecutingAssembly())
picks it up automatically), connect, and the LLM can now call game-ping / game-scene-tree-summary
against your live game. Real example outsourcing bot logic: a chess-do-turn tool that calls into your
game controller on the main thread, plus a chess-get-board tool returning a structured board model.
Same
[AiToolType]/[AiTool]/MainThread.Instance.Run(...)contract as the editor Customize Tools section β the only difference is that in a game build you register the tools and you callConnect().
Sample: a prompt and a resource
Tools are not the only thing you can expose β MCP also has prompts (reusable instruction templates an
LLM can request by name) and resources (addressable, read-only content the LLM can fetch by URI). They
register exactly like tools: a partial class decorated [AiPromptType] / [AiResourceType], with each
member decorated [AiPrompt(...)] / [AiResource(...)] and a [Description]. Register your prompt and
resource classes the same way you register tools β WithPromptsFromAssembly(...) /
WithResourcesFromAssembly(...) (or the by-type WithPrompts(...) / WithResources(...)) from the
Initialize(...) block above. Each kind is independently optional β a game can expose prompts and/or
resources with no tools at all.
using System.ComponentModel;
using com.IvanMurzak.McpPlugin; // [AiPromptType], [AiPrompt], [AiResourceType], [AiResource]
using com.IvanMurzak.McpPlugin.Common.Model; // Role, ResponseResourceContent
using com.IvanMurzak.ReflectorNet.Utils; // MainThread
using Godot;
// A PROMPT β a named, reusable instruction the LLM can request. Returns the prompt text; Role marks who
// the message is from. Set Enabled = false to ship a prompt registered-but-off until you flip it on.
[AiPromptType]
public partial class GameMcpPrompts
{
[AiPrompt(Name = "explain-game-state", Role = Role.User)]
[Description("Ask the assistant to summarize the current game state for the player.")]
public string ExplainGameState()
{
return "Read the live SceneTree via the game tools and explain the current game state in one paragraph.";
}
}
// A RESOURCE β addressable read-only content fetched by URI. Route is the URI template; the method
// returns ResponseResourceContent[]. Any Godot API access marshals onto the main thread, exactly like a tool.
[AiResourceType]
public partial class GameMcpResources
{
[AiResource(
Name = "Live SceneTree node names",
Route = "game://scene-tree/nodes",
MimeType = "application/json",
Description = "The root child node names of the live running game's SceneTree.")]
public ResponseResourceContent[] SceneTreeNodes(string uri)
{
return MainThread.Instance.Run(() =>
{
var names = new System.Collections.Generic.List<string>();
if (Engine.GetMainLoop() is SceneTree tree && tree.Root != null)
{
foreach (var child in tree.Root.GetChildren())
names.Add(child.Name);
}
var json = System.Text.Json.JsonSerializer.Serialize(names);
return new[] { ResponseResourceContent.CreateText(uri, json, "application/json") };
});
}
}Same independently-optional, manual-registration contract as tools. The
[AiPrompt]/[AiResource]attribute names,Roleenum, andResponseResourceContenthelper all come from the reusedcom.IvanMurzak.McpPluginpackage the editor path already uses β nothing Godot-specific to learn.
Where the server URL and token come from
A game build never auto-loads the editor's saved config (that's an editor-only convenience). You supply host/token one of two ways β and they compose, with env winning over code at resolution time:
In code β
builder.WithConfig(c => { c.Host = β¦; c.Token = β¦; }), as above.Out-of-band β
GODOT_MCP_*process environment variables or a project.envfile (read live byGodotMcpConfig, so a build can be reconfigured without recompiling):Environment variable
Values
Description
GODOT_MCP_CONNECTION_MODECloud/CustomConnection mode (a loopback host implies
Custom).GODOT_MCP_CLOUD_URLURL
Override the Cloud base URL (default
https://ai-game.dev).GODOT_MCP_HOSTURL
Custom-mode server host (default
http://localhost:8080).GODOT_MCP_AUTH_OPTIONNone/RequiredWhether Custom mode sends a bearer token.
GODOT_MCP_TOKENstring
The bearer token (routed to Cloud or Custom by the active mode).
GODOT_MCP_LOG_LEVELTraceβ¦NoneLog-verbosity threshold.
export GODOT_MCP_CONNECTION_MODE=Custom export GODOT_MCP_HOST=http://localhost:8080 export GODOT_MCP_AUTH_OPTION=Required export GODOT_MCP_TOKEN=your-secret-token
Cloud mode (
GodotMcpConnectionMode.Cloud) connects tohttps://ai-game.dev(override withGODOT_MCP_CLOUD_URL).Custom mode (
GodotMcpConnectionMode.Custom) connects to your own server β a local dev server, self-hosted, or a loopback address. This is the recommended mode for a shipped game (see below).
Security: opt-in only, default OFF
Exposing an MCP server inside a shipped game opens a remote-control surface: anything your registered tools can do, a connected MCP client can drive. Godot-MCP's runtime is built so this can only happen deliberately:
Opt-in only / default OFF. Building a handle does not connect. Nothing happens until your code calls
Connect(). There is no auto-connect path in a game build.Zero tools by default. A runtime with no
WithToolsβ¦call registers nothing. The attack surface is exactly the set of tools you chose to register β no more.No persisted-config auto-load. A game build never silently reads a saved config file; host/token come only from your code or
GODOT_MCP_*env /.env.Prefer loopback + a required token. For local tooling, bind to a loopback host (
http://localhost:β¦/127.0.0.1) and setAuthOption = GodotMcpAuthOption.Requiredwith a realToken. Avoid exposing the connection on a public interface in a release build unless you have explicitly designed and secured that surface.Runtime-error capture forwards sensitive data.
WithRuntimeErrorCapture()is OFF by default. When enabled, captured messages and stack traces are sent verbatim to the connected agent viaruntime-errors-getand may contain absolute paths, machine/user names, or a secret that appeared in an exception β so enable it only on a trusted loopback + token connection (full note under Capturing in-game runtime errors).
Editor-side security notes (accepted posture)
The points above are about a game build. Two editor-side surfaces are documented here for completeness; both are by design today:
Editor token storage is plaintext at rest. When you connect the editor plugin (Cloud device-auth or a Custom-mode token), the plugin persists your connection config β including the bearer token (
token) and Cloud token (cloudToken) β as plaintext JSON inuser://godot-mcp-config.json(resolved per Godot'suser://data directory). It is not encrypted or stored in an OS keystore. The trust assumption is the local user account: anyone with read access to your user data directory can read the token. Treat that file as a secret β don't commit it, sync it, or share it. To rotate, clear the saved token in the dock (or delete the file) and reconnect. (Process-env /.envoverrides viaGODOT_MCP_TOKENalways shadow the persisted value and are not written back to this file.)The dev-control bridge is unauthenticated but gated OFF. A development-only inject/control HTTP bridge exists for driving the editor dock in tests. It is unauthenticated, but its security boundary is threefold: it is editor-only (
#if TOOLS, never compiled into a game), binds127.0.0.1only, and starts only whenGODOT_MCP_DEV_CONTROL=1β a shipped addon (and any editor session without the env var) never listens. That env gate is load-bearing and enforced by a unit test + a boot-time assertion, so it can never silently ship enabled.
A short standalone copy of this contract lives in
docs/runtime-security.md.
How Godot MCP Architecture Works
Godot-MCP is a bridge between LLMs and the Godot editor. It exposes and explains Godot's tools to the LLM, which then understands the interface and uses the tools according to your requests.
On editor load, the [Tool] EditorPlugin (GodotMcpPlugin) boots the plugin: it installs a main-thread
dispatcher, builds a ReflectorNet Reflector
with Godot type converters, and opens a SignalR connection to an MCP server over the reused
com.IvanMurzak.McpPlugin client. The AI tools
it registers are then callable by any MCP-aware AI agent.
What is MCP
MCP β Model Context Protocol. In a few words, it is USB Type-C for AI, specifically for LLMs (Large
Language Models). It teaches the LLM how to use external features β such as the Godot Engine in this case,
or even your own custom C# method. Official documentation.
What is an AI agent
It is an application with a chat window. It may have smart agents to operate better, and embedded advanced MCP Tools. A well-built MCP client is 50% of the AI success in executing a task β which is why it is important to choose a good one.
What is the MCP Server
It is the bridge between the MCP Client and "something else" β in this case the Godot editor. In Cloud
mode this is the hosted ai-game.dev backend; in Custom mode it is the shared
GameDev-MCP-Server host you run yourself (or let the
addon download and run for you).
What is an MCP Tool
An MCP Tool is a function the LLM can call to interact with Godot. These tools are the bridge between
natural-language requests and actual Godot operations. When you ask the AI to "create a node" or
"open a scene," it uses MCP Tools to execute the action. Tools have typed, described parameters; return
structured results; and are thread-aware (main-thread for Godot API calls, background-thread for heavy
processing).
Building & contributing
Godot.NET.Sdk is a NuGet SDK, so no Godot binary is required to compile or unit-test:
dotnet restore Godot-MCP.sln
dotnet build Godot-MCP.sln --configuration Debug --no-restore # 0 errors required (CI gate)
dotnet test Godot-MCP.Tests/Godot-MCP.Tests.csproj --configuration Debug --no-buildA Godot 4.3+ editor is only needed for live behavioral verification of the engine-driving tools. See
CLAUDE.md for the full build/test/run
runbook, the editor-runtime assembly-load fix, conventions, and the headless testbed smoke.
Contributions are highly appreciated. Please give this project a star π if you find it useful!
π Fork the project
Clone the fork and open it in a Godot 4.3+ (mono) editor
Implement new things, commit, and push to GitHub
Create a Pull Request targeting the original Godot-MCP repository,
mainbranch.
License
Apache-2.0 Β© Ivan Murzak
This server cannot be installed
Maintenance
Latest Blog Posts
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/IvanMurzak/Godot-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server