Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/davidbuenov/dbv-mcp-server/llms.txt

Use this file to discover all available pages before exploring further.

Why explicit registration is required

Despite what Unreal’s unreal.ToolsetDefinition documentation may suggest about UHT and the runtime UToolRegistry, there is no automatic reflection-based scan that discovers subclasses of UToolsetDefinition. The engine source makes this explicit. Inside ToolsetRegistrySubsystem.cpp (the ToolsetRegistry plugin), Initialize() contains exactly one registration call:
void UToolsetRegistrySubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    Super::Initialize(Collection);
    // ...
    UToolsetRegistry::RegisterToolsetClass(UAgentSkillToolset::StaticClass());
}
The subsystem only auto-registers its own UAgentSkillToolset. Every other toolset — EditorToolset, AutomationTestToolset, SlateInspectorToolset, and any custom toolset you write — is registered explicitly by its own module’s startup code.
Without calling RegisterToolsetClass, your custom toolset will compile correctly and load into the Editor without any errors or warnings. It will never appear in list_toolsets — even if .uproject and .Build.cs are configured correctly. This is a silent failure. The only symptom is that RunPython, GetActors, or any other tool you defined simply does not show up in the tools list.

The fix: custom module implementation

The default project entry point generated by Epic contains an empty FDefaultGameModuleImpl. You need to replace it with a custom IModuleInterface subclass that hooks OnPostEngineInit and calls RegisterToolsetClass. Why OnPostEngineInit and not StartupModule?
UToolsetRegistrySubsystem (and therefore UToolsetRegistry) does not exist yet during StartupModule(). Attempting to register at that point fails silently. OnPostEngineInit is the earliest safe point where the subsystem is guaranteed to be initialized.

Registration code

Replace the contents of Source/<YourProjectName>/<YourProjectName>.cpp with the following, substituting <YourProjectName> and the toolset class name as appropriate:
// Copyright Epic Games, Inc. All Rights Reserved.

#include "<YourProjectName>.h"
#include "Modules/ModuleManager.h"
#include "MyCustomSceneToolset.h"
#include "Misc/CoreDelegates.h"

#if WITH_EDITOR
#include "ToolsetRegistry/UToolsetRegistry.h"
#endif

class F<YourProjectName>Module : public FDefaultGameModuleImpl
{
public:
    virtual void StartupModule() override
    {
        FDefaultGameModuleImpl::StartupModule();
#if WITH_EDITOR
        // Registration must wait for OnPostEngineInit:
        // UToolsetRegistrySubsystem (and UToolsetRegistry) does not exist yet
        // during StartupModule().
        FCoreDelegates::GetOnPostEngineInit().AddRaw(
            this, &F<YourProjectName>Module::OnPostEngineInit
        );
#endif
    }

    virtual void ShutdownModule() override
    {
#if WITH_EDITOR
        if (UToolsetRegistry::IsAvailable())
        {
            UToolsetRegistry::UnregisterToolsetClass(UMyCustomSceneToolset::StaticClass());
        }
#endif
        FDefaultGameModuleImpl::ShutdownModule();
    }

private:
    void OnPostEngineInit()
    {
#if WITH_EDITOR
        UToolsetRegistry::RegisterToolsetClass(UMyCustomSceneToolset::StaticClass());
#endif
    }
};

IMPLEMENT_PRIMARY_GAME_MODULE( F<YourProjectName>Module, <YourProjectName>, "<YourProjectName>" );

DEFINE_LOG_CATEGORY(Log<YourProjectName>)

Key points

  • OnPostEngineInit, not StartupModule — the subsystem is not available earlier; registration before this point fails silently.
  • UnregisterToolsetClass in ShutdownModule — prevents dangling references when Live Coding or hot-reload unloads and reloads the module. Without this, reloading can leave a stale pointer in the registry.
  • Multiple toolsets — call RegisterToolsetClass once per class inside the same OnPostEngineInit():
    void OnPostEngineInit()
    {
    #if WITH_EDITOR
        UToolsetRegistry::RegisterToolsetClass(UMyCustomSceneToolset::StaticClass());
        UToolsetRegistry::RegisterToolsetClass(UMyOtherToolset::StaticClass());
    #endif
    }
    
  • No regeneration needed — this change touches only <YourProjectName>.cpp, not .Build.cs or .uproject. A Live Coding recompile (Ctrl+Alt+F11) is sufficient after this edit.

Before and after

Before registrationlist_toolsets returns only the built-in subsystem toolset, with no trace of your custom class:
{
  "toolsets": [
    { "name": "ToolsetRegistry.AgentSkillToolset" }
  ]
}
After registration — your toolset appears alongside the built-in ones, and its AICallable methods are discoverable as tools:
{
  "toolsets": [
    { "name": "ToolsetRegistry.AgentSkillToolset" },
    { "name": "<YourProjectName>.MyCustomSceneToolset",
      "tools": ["CountActorsWithMesh", "RunPython"] }
  ]
}

Verifying in the Output Log

When registration succeeds, Unreal logs the following line in the Output Log during Editor startup:
LogToolsetRegistry: Display: Registering Toolset <YourProjectName>.UMyCustomSceneToolset
If this line is absent after a full rebuild, check that <YourProjectName>.cpp contains the custom module class with the OnPostEngineInit callback. That missing callback is the cause in virtually every case of this silent failure.

Build docs developers (and LLMs) love