195 lines
6.9 KiB
Markdown
195 lines
6.9 KiB
Markdown
|
|
# 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<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:
|
|
|
|
```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: <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`
|