10 KiB
Unreal MCP — Claude Code Project Context
Ce fichier est lu automatiquement par Claude Code au démarrage de chaque session. Il documente l'architecture, les décisions techniques et le travail en cours.
Architecture globale
Le système est composé de 3 couches :
Claude Code (stdio)
↕ JSON via stdio
Python FastMCP server (unreal_mcp_server.py)
↕ JSON via TCP socket (connexion par commande)
C++ UEditorSubsystem (UnrealMCPBridge)
↕ GameThread via AsyncTask + Promise/Future
Unreal Engine Editor (port TCP 55557, localhost)
Détails par couche
Claude Code — lit .claude/settings.json pour démarrer le serveur MCP Python via uv run.
Python FastMCP server (unreal_mcp_server.py) :
- Utilise
FastMCPavec transport stdio - Classe
UnrealConnection: crée une nouvelle socket TCP par commande (connexion-par-commande, pas persistante) - Timeout de 5 secondes sur réception
- 5 modules d'outils :
editor_tools,blueprint_tools,node_tools,project_tools,umg_tools
C++ plugin (UnrealMCPBridge — UEditorSubsystem) :
- S'initialise automatiquement au démarrage de l'éditeur via
Initialize() StartServer()→ crée un listener TCP sur127.0.0.1:55557→ spawn d'un threadFMCPServerRunnableExecuteCommand(): utiliseAsyncTask(ENamedThreads::GameThread, ...)avecTPromise<FString>/TFuture<FString>— le thread réseau bloque jusqu'à la réponse du Game Thread- 5 catégories de handlers : Editor, Blueprint, BlueprintNode, Project, UMG
Structure des fichiers (plugin)
Plugins/UnrealMCP/
├── Source/UnrealMCP/
│ ├── Public/
│ │ ├── UnrealMCPBridge.h — UEditorSubsystem, déclaration
│ │ ├── UnrealMCPModule.h — IModuleInterface
│ │ ├── MCPServerRunnable.h — FRunnableThread TCP listener
│ │ ├── Setup/
│ │ │ └── UnrealMCPSetupCommands.h — [À CRÉER] menu "Claude Code"
│ │ └── Commands/
│ │ ├── UnrealMCPEditorCommands.h
│ │ ├── UnrealMCPBlueprintCommands.h
│ │ ├── UnrealMCPBlueprintNodeCommands.h
│ │ ├── UnrealMCPProjectCommands.h
│ │ ├── UnrealMCPUMGCommands.h
│ │ └── UnrealMCPCommonUtils.h
│ └── Private/
│ ├── UnrealMCPBridge.cpp — logique principale, ExecuteCommand()
│ ├── UnrealMCPModule.cpp — StartupModule / ShutdownModule
│ ├── MCPServerRunnable.cpp — thread TCP, ProcessMessage()
│ ├── Setup/
│ │ └── UnrealMCPSetupCommands.cpp — [À CRÉER] OnInitClaudeCode()
│ └── Commands/
│ ├── UnrealMCPEditorCommands.cpp
│ ├── UnrealMCPBlueprintCommands.cpp
│ ├── UnrealMCPBlueprintNodeCommands.cpp
│ ├── UnrealMCPProjectCommands.cpp
│ ├── UnrealMCPUMGCommands.cpp
│ └── UnrealMCPCommonUtils.cpp
├── PythonServer/ — [À CRÉER] copie du serveur Python bundlé dans le plugin
│ ├── unreal_mcp_server.py
│ ├── pyproject.toml
│ ├── uv.lock
│ └── tools/
│ ├── editor_tools.py
│ ├── blueprint_tools.py
│ ├── node_tools.py
│ ├── project_tools.py
│ └── umg_tools.py
└── UnrealMCP.uplugin
Feature en cours : "Init Claude Code" menu
Objectif
Ajouter un menu "Claude Code" dans la barre de menu de l'éditeur UE. Ce menu contient un bouton "Init Claude Code" (tooltip: "Initialize Claude Code for this project (requires Claude Code restart)").
En cliquant, le plugin :
- Vérifie que
uvest disponible dans le PATH - Vérifie si
.claude/existe déjà (demande confirmation si oui) - Copie
<PluginDir>/PythonServer/→<ProjectDir>/.claude/mcp-server/ - Écrit
<ProjectDir>/.claude/settings.json(seulement si inexistant) - Écrit
<ProjectDir>/.claude/settings.local.json(toujours — permissions safe) - Affiche un dialog de succès
Contenu de settings.json généré (chemin relatif — portable)
{
"mcpServers": {
"unrealMCP": {
"type": "stdio",
"command": "uv",
"args": ["--directory", ".claude/mcp-server", "run", "unreal_mcp_server.py"]
}
}
}
Note : Le chemin .claude/mcp-server est relatif au répertoire projet (là où Claude Code est lancé). C'est ce qui rend la config portable entre machines — pas de chemin absolu.
Fichiers C++ à créer
Public/Setup/UnrealMCPSetupCommands.h
#pragma once
#include "CoreMinimal.h"
class FUnrealMCPSetupCommands
{
public:
static void RegisterMenus();
private:
static void OnInitClaudeCode();
static void BuildMenu(UToolMenu* Menu);
static bool IsUvAvailable();
static bool CopyPythonServer(const FString& Src, const FString& Dest);
static bool WriteClaudeSettings(const FString& ClaudeDir);
static bool WriteClaudeLocalSettings(const FString& ClaudeDir);
};
Private/Setup/UnrealMCPSetupCommands.cpp — points clés :
// Menu bar extension
UToolMenu* MenuBar = UToolMenus::Get()->ExtendMenu("MainFrame.MainTabMenu");
FToolMenuSection& Section = MenuBar->FindOrAddSection("ClaudeCode");
FToolMenuEntry SubMenu = FToolMenuEntry::InitSubMenu(
"ClaudeCodeMenu",
LOCTEXT("ClaudeCodeMenuLabel", "Claude Code"),
LOCTEXT("ClaudeCodeMenuTooltip", "Claude Code tools"),
FNewToolMenuDelegate::CreateStatic(&FUnrealMCPSetupCommands::BuildMenu)
);
Section.AddEntry(SubMenu);
// Chemin source = plugin bundled PythonServer
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin("UnrealMCP");
const FString PythonServerSrc = Plugin->GetBaseDir() / TEXT("PythonServer");
const FString McpServerDest = FPaths::ProjectDir() / TEXT(".claude/mcp-server");
// Copie via IPlatformFile
IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile();
PF.CopyDirectoryTree(*McpServerDest, *PythonServerSrc, true);
// Check uv via FPlatformProcess::ExecProcess
int32 ReturnCode = 0;
FString StdOut, StdErr;
FPlatformProcess::ExecProcess(TEXT("uv"), TEXT("--version"), &ReturnCode, &StdOut, &StdErr);
// ReturnCode == 0 → uv présent
UnrealMCPModule.cpp — enregistrement du callback :
void FUnrealMCPModule::StartupModule()
{
if (GEditor)
{
UToolMenus::RegisterStartupCallback(
FSimpleMulticastDelegate::FDelegate::CreateRaw(
this, &FUnrealMCPModule::RegisterMenus));
}
}
void FUnrealMCPModule::ShutdownModule()
{
UToolMenus::UnRegisterStartupCallback(this);
}
void FUnrealMCPModule::RegisterMenus()
{
FUnrealMCPSetupCommands::RegisterMenus();
}
UnrealMCPModule.h — ajouter :
private:
void RegisterMenus();
Dépendances Build.cs déjà présentes
ToolMenus✅ (dans le blocbBuildEditor)Projects✅ (pourIPluginManager)Slate,SlateCore✅
Problèmes connus et workarounds
spawn_blueprint_actor → Timeout systématique
Cause : create_blueprint + compile_blueprint crée les blueprints en mémoire UE mais ne les sauvegarde pas sur disque (pas de .uasset). spawn_blueprint_actor tente de charger depuis le disque → attend indéfiniment → timeout.
Workaround : Utiliser spawn_actor avec "StaticMeshActor" puis assigner le mesh manuellement dans l'éditeur.
set_actor_property("StaticMesh", ...) → "Property not found"
Cause : La propriété StaticMesh est sur le composant StaticMeshComponent, pas directement sur l'acteur AStaticMeshActor. Les chemins de type "StaticMeshComponent.StaticMesh" ne fonctionnent pas non plus via MCP.
Workaround : set_static_mesh_properties fonctionne seulement sur les composants définis dans un Blueprint, pas sur des instances level.
spawn_actor avec nom de Blueprint → "Unknown actor type"
Cause : spawn_actor n'accepte que les types built-in UE ("StaticMeshActor", "PointLight", etc.). Les noms de classe Blueprint ne sont pas supportés.
Requêtes parallèles massives → Timeout
Cause : Trop de connexions TCP simultanées saturent le serveur. Workaround : Appels séquentiels ou par petits groupes (max ~5 en parallèle).
Configuration MCP active
.claude/settings.json (chemin à adapter dans le nouveau projet) :
{
"mcpServers": {
"unrealMCP": {
"type": "stdio",
"command": "uv",
"args": ["--directory", "<chemin-vers-Python-server>", "run", "unreal_mcp_server.py"]
}
}
}
.claude/settings.local.json — 36 outils MCP pré-approuvés (déjà en place dans ce projet).
Outils MCP disponibles (36)
get_actors_in_level, find_actors_by_name, spawn_actor, delete_actor,
set_actor_transform, get_actor_properties, set_actor_property,
spawn_blueprint_actor, focus_viewport, take_screenshot,
create_blueprint, add_component_to_blueprint, set_static_mesh_properties,
set_component_property, set_physics_properties, set_blueprint_property,
set_pawn_properties, compile_blueprint, find_blueprint_nodes,
add_blueprint_event_node, add_blueprint_input_action_node,
add_blueprint_function_node, add_blueprint_variable,
add_blueprint_self_reference, add_blueprint_get_self_component_reference,
connect_blueprint_nodes, create_input_mapping,
create_umg_widget_blueprint, add_text_block_to_widget,
add_button_to_widget, bind_widget_event, set_text_block_binding,
add_widget_to_viewport
Prochaines étapes
- Créer
PythonServer/dans le plugin — copie des fichiers Python depuisPython/(ou équivalent dans la nouvelle structure) - Créer
UnrealMCPSetupCommands.het.cpp— voir design ci-dessus - Modifier
UnrealMCPModule.het.cpp— enregistrer le callbackRegisterMenus - Compiler le plugin — vérifier que le menu "Claude Code" apparaît dans l'éditeur
- Tester "Init Claude Code" — depuis un projet vierge avec le plugin en submodule