# 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: ```cpp #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: ```cpp void FMCPServer::HandleFoo(const FJsonObject* Json, FJsonObject* Result) { FString Name = Json->GetStringField(TEXT("name")); // ... body ... } ``` Becomes: ```cpp 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>`). For JSON objects use `FMCPJsonObject` (has `.Json` field of type `TSharedPtr`). - **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: ```cpp // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- ``` ## 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: ```cpp // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // BUG: // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ``` 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`