Custom tools

Add your own AI tools, RQL modules, prompts and resources to Unreal MCP from any C++ plugin, and expose gameplay tools in a running game.

Updated November 4, 2026 View as Markdown

Unreal MCP lets any Unreal plugin, including your own game project's plugins, add capabilities for AI agents. You do not fork Unreal MCP: you implement a small public interface, register it as an Unreal modular feature, and the plugin picks it up when your module loads.

This page is for C++ developers. If you only want more engine coverage, check the ready-made extensions first.

Pick the right kind of contribution

You want the agent to…ContributeInterface
Read or change editor data for an engine feature or your own typesAn RQL module (recommended for editor work)IUnrealMcpRqlModule
Run an action that is not data access, or act inside a running gameToolsIUnrealMcpToolProvider
Fetch a reusable prompt templatePromptsIUnrealMcpPromptProvider
Read fixed content by URIResourcesIUnrealMcpResourceProvider

All first-party extensions are RQL modules, so the agent reaches them through the same rql-query and rql-mutate tools as the rest of the editor.

Option 1: an RQL module

Depend on the UnrealMcpRql module in your editor module's Build.cs, and declare the UnrealMCP plugin in your .uplugin Plugins array. Then implement the module:

#include "IUnrealMcpRqlModule.h"

class FMyRqlModule : public IUnrealMcpRqlModule
{
public:
    virtual FString GetRqlModuleName() const override { return TEXT("Niagara"); }
    virtual FString GetExtensionId() const override { return TEXT("com.example.niagara-ai"); }
    virtual FString GetExtensionVersion() const override { return UnrealMcpRql::PluginVersion(TEXT("MyNiagaraAI")); }
    virtual void ConfigureRql(FUnrealMcpRqlModuleContext& Context) override
    {
        Context.AddAssetSource(TEXT("niagaraSystems"), TEXT("Niagara system assets"), UNiagaraSystem::StaticClass());
        Context.AddApiSource(TEXT("niagara"), TEXT("Niagara operations"), UMyNiagaraOperations::StaticClass(),
            TEXT("NiagaraOperations"));
        Context.AddGuide(TEXT("Niagara"), TEXT("... markdown with example queries ..."));
    }
};

IMPLEMENT_MODULE(TUnrealMcpRqlEditorModule<FMyRqlModule>, MyNiagaraAI)

What the context can add:

  • Sources (where a query starts): AddObjectSource, AddObjectListSource, AddAssetSource, AddClassDefaultsSource.
  • Operations (called with call): AddApiClass or AddApiSource make every UFUNCTION of a class callable. Declare them UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true")); the doc comment above each becomes the description the agent reads.
  • Members: ExposeProperties for properties reflection hides, and read-only computed members with AddJsonMember, AddObjectMember, AddObjectListMember.
  • Guides: AddGuide adds a section to the unreal-rql skill.

A mutation runs in one editor transaction, so create objects with RF_Transactional and call Modify() on what you change. The editor's Extensions section lists your module and can disable it. The buildable reference is the samples/UnrealAITemplate plugin in the Unreal-MCP repository.

Option 2: custom tools

Step 1: add the dependency

In your module's Build.cs:

PublicDependencyModuleNames.AddRange(new string[] { "Core" });

PrivateDependencyModuleNames.AddRange(new string[]
{
    "CoreUObject", "Engine", "Projects",
    "Json",            // the registry header includes Dom/JsonObject.h
    "UnrealMcpEditor", // the extension contract and tool registry
});

In your .uplugin, add the plugin dependency so load order resolves:

{
  "Plugins": [ { "Name": "UnrealMCP", "Enabled": true } ]
}

Step 2: implement the provider

#include "IUnrealMcpToolProvider.h"
#include "UnrealMcpToolRegistry.h"

class FMyExtensionProvider : public IUnrealMcpToolProvider
{
public:
    virtual FString GetExtensionId() const override      { return TEXT("com.foo.my-extension"); }
    virtual FText   GetDisplayName() const override      { return NSLOCTEXT("Foo", "Name", "My Extension"); }
    virtual FString GetExtensionVersion() const override { return TEXT("1.0.0"); }

    virtual void RegisterTools(FUnrealMcpToolRegistry& Registry) override
    {
        Registry.Tool(TEXT("hello-extension"))
            .Title(TEXT("Hello Extension"))
            .Description(TEXT("Returns a friendly greeting."))
            .ParamString(TEXT("name"), TEXT("Who to greet. Defaults to 'world'."))
            .ReadOnlyHint(true).DestructiveHint(false).IdempotentHint(true).OpenWorldHint(false)
            .Handle([](const FUnrealMcpToolCall& Call) -> FUnrealMcpToolResult
            {
                const FString Name = Call.Has(TEXT("name")) ? Call.GetString(TEXT("name")) : TEXT("world");
                return FUnrealMcpToolResult::Success(FString::Printf(TEXT("Hello, %s!"), *Name));
            });
    }
};

The builder offers .Title, .Description, the parameter helpers .ParamString, .ParamInt, .ParamNumber, .ParamBool and .ParamVector (pass EUnrealMcpParamRequirement::Required for a required one), the four MCP hints, and .Handle. The handler runs on the game thread, so you can call editor and UObject APIs directly.

Rules that matter:

  • Tool ids are kebab-case: lowercase letters and digits separated by single hyphens.
  • Give every tool a title and all four hints. Mark anything that deletes or overwrites as destructive.
  • Do not call .ExtensionId(...) yourself; your GetExtensionId() is stamped on automatically.

Step 3: register it as a modular feature

virtual void StartupModule() override
{
    Provider = MakeUnique<FMyExtensionProvider>();
    IModularFeatures::Get().RegisterModularFeature(
        IUnrealMcpToolProvider::GetModularFeatureName(), Provider.Get());
}

virtual void ShutdownModule() override
{
    if (Provider.IsValid())
    {
        IModularFeatures::Get().UnregisterModularFeature(
            IUnrealMcpToolProvider::GetModularFeatureName(), Provider.Get());
        Provider.Reset();
    }
}

Keep the provider alive while it is registered and always unregister it on shutdown.

How registration behaves

  • Discovery and hot reload. Providers are found at editor start. Loading or unloading your module later rebuilds the registry and the AI clients see the change automatically.
  • Deterministic order. Providers merge in ascending extension-id order; tools inside one provider keep their declaration order.
  • Isolation. An invalid tool (bad id, broken schema, no handler) or a duplicate tool name is dropped and recorded on your extension's entry. Your other tools and other extensions are unaffected. A crash inside a handler is still an editor crash, so validate inputs and return FUnrealMcpToolResult::Error(...).
  • Visibility and toggles. Your tools get the same Visible checkbox in the MCP Tools window as built-in ones. A disabled extension contributes nothing.

Prompts and resources

Prompts and resources use the same pattern with IUnrealMcpPromptProvider plus FUnrealMcpPromptRegistry, and IUnrealMcpResourceProvider plus FUnrealMcpResourceRegistry. A prompt declares parameters with the same Param* helpers and returns role-tagged messages. A resource is identified by its URI and returns either text or a base64 blob with a MIME type. Only fixed URIs are supported today, not templated ones. The built-in level-design-brief prompt and the unreal://project/levels resource are working references.

Generate a tool with the agent

With a source install of the plugin, the unreal-skill-create system tool writes a new C++ tool file into the plugin's editor module. The tool exists only after you rebuild the editor (Live Coding or a project rebuild).

In-game (runtime) tools

Unreal MCP can also connect a running game (Play In Editor, Standalone or a packaged Development build) so an AI agent can drive it. A runtime connection offers no engine tools: only the tools your game registers.

  1. Make your game module depend on UnrealMcpRuntime (not UnrealMcpEditor) and author tools exactly as above.
  2. Enable Project Settings, Plugins, Unreal MCP (Runtime). The switch is off by default and is stored in DefaultGame.ini.
  3. Connect explicitly. The game never connects on its own.

From C++:

#include "UnrealMcpRuntimeSubsystem.h"

if (UUnrealMcpRuntimeSubsystem* Mcp = UUnrealMcpRuntimeSubsystem::Get(this))
{
    Mcp->RegisterToolProvider(MyToolProvider);
    Mcp->Connect(TEXT("http://localhost:8080"), TEXT("my-token"));
}

From Blueprint, use Get Unreal MCP Runtime Subsystem, then Connect, Disconnect and Is Connected. From the console:

UnrealMcp.Connect <host> [token]
UnrealMcp.Disconnect

Safety rules built into Connect():

  • Shipping builds refuse to connect unless you compile in the bUnrealMcpAllowShipping flag.
  • Only loopback hosts are allowed unless you pass bAllowRemoteHost = true.
  • Runtime mode works on desktop platforms only (Windows, macOS, Linux).

The samples/UnrealAIRuntimeSample plugin in the repository adds a game-time-dilation tool that reads and sets the world's time dilation in a running game. If you do not need runtime tools, exclude the plugin from packaged builds as described on Installation.