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 structsMCPUtils.h— utility functions available to handlersMCPAssetFinder.h— asset lookup helpers (FindAsset, LoadAsset, etc.)MCPHandlers_Interfaces.h— a clean example of new-style handlersMCPHandlers_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 aUPROPERTYfield on the class. The framework populates these automatically before callingHandle(). -
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 useFMCPJsonArray(has.Arrayfield of typeTArray<TSharedPtr<FJsonValue>>). For JSON objects useFMCPJsonObject(has.Jsonfield of typeTSharedPtr<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 useJson->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:
Another agent will deal with it later.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // BUG: <description of the bug and what needs to change> // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - 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:
MCPHandlers_Read.cppMCPHandlers_Discovery.cppMCPHandlers_Graphs.cppMCPHandlers_Variables.cppMCPHandlers_Params.cppMCPHandlers_Dispatchers.cppMCPHandlers_Components.cppMCPHandlers_Snapshot.cppMCPHandlers_MaterialRead.cppMCPHandlers_MaterialMutation.cppMCPHandlers_MaterialInstance.cppMCPHandlers_StateMachine.cpp