Files
integration/refactor.md

6.9 KiB

Handler Refactor Instructions

Plugins/BlueprintMCP/Source/BlueprintMCP contains an MCP server that allows Claude Code to control Unreal functions. MCP servers accept different commands; each command has a "handler."

We are converting all handlers from an old style to a new style. In the old style, each handler is a method of FMCPServer. In the new style, each handler is a UCLASS derived from IMCPHandler. You will be assigned one source file to convert. Only modify that one file.

Step 1: Study the Existing Code

Before editing, read these files carefully:

  • MCPHandler.h — the IMCPHandler interface and marker structs
  • MCPUtils.h — utility functions available to handlers
  • MCPAssetFinder.h — asset lookup helpers (FindAsset, LoadAsset, etc.)
  • MCPHandlers_Interfaces.h — a clean example of new-style handlers
  • MCPHandlers_Validation.h — another example (shows Optional params, bool, int32)
  • MCPHandlers_AnimMutation.h — another example (shows FMCPJsonArray)

All of these are in Plugins/BlueprintMCP/Source/BlueprintMCP/.

Step 2: Convert Your File

Each old-style handler function becomes a new-style UCLASS. The mechanical steps are:

2a. Change the file header

Replace the old includes with this pattern:

#pragma once

#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPUtils.h"
// ... any other includes the handlers actually need ...
#include "MCPHandlers_YourFile.generated.h"

Remove #include "MCPServer.h" — new-style handlers don't need it.

Remove any includes that are no longer used after conversion. Keep only what the handler bodies actually reference.

2b. Wrap each handler in a UCLASS

Each old-style function like this:

void FMCPServer::HandleFoo(const FJsonObject* Json, FJsonObject* Result)
{
    FString Name = Json->GetStringField(TEXT("name"));
    // ... body ...
}

Becomes:

UCLASS(meta=(ToolName="the_mcp_tool_name"))
class UMCPHandler_Foo : public UObject, public IMCPHandler
{
    GENERATED_BODY()

public:
    UPROPERTY(meta=(Description="Human-readable description of this parameter"))
    FString Name;

    virtual FString GetDescription() const override
    {
        return TEXT("Human-readable description of what this tool does.");
    }

    virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override
    {
        // ... body (minus the parameter extraction) ...
    }
};

2c. Convert parameters to UPROPERTY fields

  • Each Json->GetStringField(TEXT("foo")) at the top of the handler becomes a UPROPERTY field on the class. The framework populates these automatically before calling Handle().

  • Naming rule: The UPROPERTY field name's first character gets lowercased to produce the JSON key. So FString Blueprint; maps to JSON key "blueprint". FString PackagePath; maps to "packagePath". Choose field names so that this automatic mapping produces the correct JSON key (i.e. the same key the old code was reading).

  • Required vs optional: Fields are required by default. Add meta=(Optional, Description="...") for optional parameters. Optional fields should have default values (e.g. bool Foo = false;, int32 Limit = 0;, FString Bar; — empty string is the default).

  • Supported types: FString, bool, int32, float, FName. For JSON arrays use FMCPJsonArray (has .Array field of type TArray<TSharedPtr<FJsonValue>>). For JSON objects use FMCPJsonObject (has .Json field of type TSharedPtr<FJsonObject>).

  • Remove manual extraction: Delete the Json->GetStringField(), Json->GetBoolField(), etc. lines that read parameters. The UPROPERTY fields already contain the values. You can still use Json->HasField() to check whether an optional field was actually provided (for cases where you need to distinguish "not provided" from "provided as default value").

  • Remove manual validation of required fields: The framework already returns an error if a required UPROPERTY field is missing from JSON. Delete checks like if (Name.IsEmpty()) return MakeErrorJson(...) for required FString fields. However, keep validation that checks field content (e.g. "must start with /Game").

2d. Avoid UPROPERTY / local variable name conflicts

If the old code has a local variable with the same name as your new UPROPERTY field (e.g. UPROPERTY FString Blueprint; and local UBlueprint* Blueprint), rename the local variable. Convention: append Obj — e.g. UBlueprint* BlueprintObj, USkeleton* SkeletonObj.

2e. Tool name

The ToolName in the UCLASS meta must exactly match the tool name string that was used in the old-style H(TEXT("tool_name"), ...) registration in MCPServer.cpp. If you're unsure, search MCPServer.cpp for the old handler function name to find the tool name.

2f. GetDescription()

Write a concise 1-2 sentence description of what the tool does. This is shown to the LLM that calls the tool.

2g. Separator between classes

Put this separator between each UCLASS:

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

Step 3: Rename the File

After conversion, rename the file from .cpp to .h. The file is now a header — UHT will process the GENERATED_BODY() macros during build.

What NOT to Do

  • Do NOT modify MCPServer.h or MCPServer.cpp. We will clean up old registrations separately.
  • Do NOT modify MCPHandlers.cpp (the include aggregator). We will add the new include separately.
  • Do NOT add #include "MCPServer.h" — new-style handlers are self-registering via UHT reflection and don't need it.
  • Do NOT change the handler logic. Convert the structure, not the behavior. This is a mechanical refactor. If you see a bug that can be fixed within your file (e.g. a typo), fix it. But if you see a bug that would require editing other files to fix, leave the code as-is and insert a prominent comment block like this:
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // BUG: <description of the bug and what needs to change>
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    Another agent will deal with it later.
  • Do NOT rename JSON output fields. The tool's output format must stay identical.
  • Do NOT add or remove handlers. Convert exactly what's there.

Files to Convert

Each of these .cpp files contains old-style handlers to convert:

  1. MCPHandlers_Read.cpp
  2. MCPHandlers_Discovery.cpp
  3. MCPHandlers_Graphs.cpp
  4. MCPHandlers_Variables.cpp
  5. MCPHandlers_Params.cpp
  6. MCPHandlers_Dispatchers.cpp
  7. MCPHandlers_Components.cpp
  8. MCPHandlers_Snapshot.cpp
  9. MCPHandlers_MaterialRead.cpp
  10. MCPHandlers_MaterialMutation.cpp
  11. MCPHandlers_MaterialInstance.cpp
  12. MCPHandlers_StateMachine.cpp