adding MCPPort in project settings

This commit is contained in:
ange committed 2026-04-26 20:07:11 +02:00
1 parent 0e8cb5957a
commit ed4c724b9f
8 files changed
+177 -10

No files matched your search

+2 -1
View File
@@ -5,6 +5,7 @@ A simple MCP server for interacting with Unreal Engine.
"""
import logging
import os
import socket
import sys
import json
@@ -25,7 +26,7 @@ logger = logging.getLogger("UnrealMCP")
# Configuration
UNREAL_HOST = "127.0.0.1"
UNREAL_PORT = 55557
UNREAL_PORT = int(os.environ.get("UNREAL_MCP_PORT", "55557"))
class UnrealConnection:
"""Connection to an Unreal Engine instance."""
+3 -3
View File
@@ -59,9 +59,9 @@
#include "Commands/UnrealMCPCommonUtils.h"
#include "Commands/UnrealMCPUMGCommands.h"
// Default settings
#include "UnrealMCPSettings.h"
#define MCP_SERVER_HOST "127.0.0.1"
#define MCP_SERVER_PORT 55557
UUnrealMCPBridge::UUnrealMCPBridge()
{
@@ -90,7 +90,7 @@ void UUnrealMCPBridge::Initialize(FSubsystemCollectionBase& Collection)
ListenerSocket = nullptr;
ConnectionSocket = nullptr;
ServerThread = nullptr;
Port = MCP_SERVER_PORT;
Port = UUnrealMCPSettings::Get()->MCPPort;
FIPv4Address::Parse(MCP_SERVER_HOST, ServerAddress);
// Start the server automatically
@@ -0,0 +1,77 @@
#include "UnrealMCPSettings.h"
#include "UnrealMCPLog.h"
#if WITH_EDITOR
#include "UnrealEdMisc.h"
#include "HAL/PlatformFileManager.h"
#include "Misc/FileHelper.h"
#include "Misc/MessageDialog.h"
#include "Misc/Paths.h"
#define LOCTEXT_NAMESPACE "UUnrealMCPSettings"
void UUnrealMCPSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.GetPropertyName() != GET_MEMBER_NAME_CHECKED(UUnrealMCPSettings, MCPPort))
{
return;
}
// Write to Config/DefaultGame.ini (the persistent project config, not Saved/).
// SaveConfig() alone writes to Saved/Config/ which can be wiped between sessions.
UpdateDefaultConfigFile();
const FString McpJsonPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()) / TEXT(".mcp.json");
if (!FPaths::FileExists(McpJsonPath))
{
// Init not done yet — .mcp.json will be created with the right port when Init Claude Code is run
return;
}
UpdateMcpJson();
const FText RestartMessage = FText::FromString(FString::Printf(
TEXT("Le port MCP a été modifié → %d\n\n")
TEXT("Le fichier .mcp.json a été mis à jour.\n")
TEXT("Un redémarrage de l'éditeur est nécessaire pour que le bridge C++ écoute sur le nouveau port.\n")
TEXT("Redémarrer maintenant ?"),
MCPPort));
const EAppReturnType::Type Answer = FMessageDialog::Open(EAppMsgType::YesNo, RestartMessage);
if (Answer == EAppReturnType::Yes)
{
FUnrealEdMisc::Get().RestartEditor(false);
}
}
void UUnrealMCPSettings::UpdateMcpJson() const
{
const FString FilePath = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()) / TEXT(".mcp.json");
const FString Content = FString::Printf(
TEXT("{\n")
TEXT(" \"mcpServers\": {\n")
TEXT(" \"unrealMCP\": {\n")
TEXT(" \"type\": \"stdio\",\n")
TEXT(" \"command\": \"uv\",\n")
TEXT(" \"args\": [\"--directory\", \".claude/mcp-server\", \"run\", \"unreal_mcp_server.py\"],\n")
TEXT(" \"env\": {\n")
TEXT(" \"UNREAL_MCP_PORT\": \"%d\"\n")
TEXT(" }\n")
TEXT(" }\n")
TEXT(" }\n")
TEXT("}\n"),
MCPPort);
if (FFileHelper::SaveStringToFile(Content, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
{
UE_LOG(LogUnrealMCP, Display, TEXT("[UUnrealMCPSettings] .mcp.json updated with port %d"), MCPPort);
}
else
{
UE_LOG(LogUnrealMCP, Error, TEXT("[UUnrealMCPSettings] Failed to write .mcp.json at %s"), *FilePath);
}
}
#undef LOCTEXT_NAMESPACE
#endif
@@ -0,0 +1,30 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine/DeveloperSettings.h"
#include "UnrealMCPSettings.generated.h"
UCLASS(Config=Game, meta=(DisplayName="Unreal MCP"))
class UNREALMCP_API UUnrealMCPSettings : public UDeveloperSettings
{
GENERATED_BODY()
public:
virtual FName GetCategoryName() const override { return TEXT("Plugins"); }
virtual FName GetSectionName() const override { return TEXT("UnrealMCP"); }
UPROPERTY(Config, EditAnywhere, Category="Server",
meta=(ClampMin=1024, ClampMax=65535,
DisplayName="MCP Server Port",
ToolTip="Port TCP sur lequel le bridge Unreal MCP écoute. Doit correspondre à UNREAL_MCP_PORT dans .mcp.json. Nécessite un redémarrage de l'éditeur pour être pris en compte."))
int32 MCPPort = 55557;
static const UUnrealMCPSettings* Get() { return GetDefault<UUnrealMCPSettings>(); }
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
private:
void UpdateMcpJson() const;
#endif
};
@@ -1,5 +1,6 @@
#include "ClaudeCodeInitializer.h"
#include "UnrealMCPSetupLog.h"
#include "UnrealMCPSettings.h"
#include "ClaudeCodeResultDialog.h"
#include "Dom/JsonObject.h"
#include "HAL/FileManager.h"
@@ -10,6 +11,7 @@
#include "Misc/MessageDialog.h"
#include "Misc/Paths.h"
#include "Misc/ScopedSlowTask.h"
#include "UnrealEdMisc.h"
#include "Interfaces/IPluginManager.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
@@ -88,6 +90,19 @@ void FClaudeCodeInitializer::Run()
}
FClaudeCodeResultDialog::Show(Result);
if (Result.bOverallSuccess)
{
const EAppReturnType::Type RestartAnswer = FMessageDialog::Open(EAppMsgType::YesNo,
LOCTEXT("RestartPrompt",
"Initialisation réussie !\n\n"
"Redémarrez l'éditeur pour que Claude Code détecte la configuration MCP.\n"
"Redémarrer maintenant ?"));
if (RestartAnswer == EAppReturnType::Yes)
{
FUnrealEdMisc::Get().RestartEditor(false);
}
}
}
bool FClaudeCodeInitializer::IsCppProject()
@@ -143,14 +158,33 @@ bool FClaudeCodeInitializer::IsMcpJsonValid(const FString& ProjectDir)
return false;
}
bool bFoundScript = false;
for (const TSharedPtr<FJsonValue>& Arg : *Args)
{
if (Arg->AsString() == TEXT("unreal_mcp_server.py"))
{
return true;
bFoundScript = true;
break;
}
}
return false;
if (!bFoundScript)
{
return false;
}
// Validate env.UNREAL_MCP_PORT matches the configured port
const TSharedPtr<FJsonObject>* Env;
if (!(*UnrealMCP)->TryGetObjectField(TEXT("env"), Env))
{
return false;
}
FString PortStr;
if (!(*Env)->TryGetStringField(TEXT("UNREAL_MCP_PORT"), PortStr))
{
return false;
}
const int32 ConfiguredPort = UUnrealMCPSettings::Get()->MCPPort;
return FCString::Atoi(*PortStr) == ConfiguredPort;
}
bool FClaudeCodeInitializer::IsMcpServerFolderValid(const FString& ProjectDir)
@@ -340,17 +374,22 @@ FInitStep FClaudeCodeInitializer::WriteMcpJson(const FString& ProjectDir)
}
const FString FilePath = ProjectDir / TEXT(".mcp.json");
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Writing %s"), *FilePath);
const FString Content =
const int32 Port = UUnrealMCPSettings::Get()->MCPPort;
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Writing %s (port %d)"), *FilePath, Port);
const FString Content = FString::Printf(
TEXT("{\n")
TEXT(" \"mcpServers\": {\n")
TEXT(" \"unrealMCP\": {\n")
TEXT(" \"type\": \"stdio\",\n")
TEXT(" \"command\": \"uv\",\n")
TEXT(" \"args\": [\"--directory\", \".claude/mcp-server\", \"run\", \"unreal_mcp_server.py\"]\n")
TEXT(" \"args\": [\"--directory\", \".claude/mcp-server\", \"run\", \"unreal_mcp_server.py\"],\n")
TEXT(" \"env\": {\n")
TEXT(" \"UNREAL_MCP_PORT\": \"%d\"\n")
TEXT(" }\n")
TEXT(" }\n")
TEXT(" }\n")
TEXT("}\n");
TEXT("}\n"),
Port);
if (FFileHelper::SaveStringToFile(Content, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
{
@@ -4,6 +4,7 @@
#include "ToolMenus.h"
#include "ToolMenu.h"
#include "ToolMenuSection.h"
#include "ISettingsModule.h"
#define LOCTEXT_NAMESPACE "FClaudeCodeMenuExtender"
@@ -37,6 +38,22 @@ void FClaudeCodeMenuExtender::BuildMenu(UToolMenu* Menu)
FSlateIcon(),
FUIAction(FExecuteAction::CreateStatic(&FClaudeCodeMenuExtender::OnInitClaudeCodeClicked))
);
Section.AddMenuEntry(
"OpenMCPSettings",
LOCTEXT("SettingsLabel", "Configuration du port MCP..."),
LOCTEXT("SettingsTooltip", "Ouvrir les paramètres du port MCP dans les Project Settings"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Settings"),
FUIAction(FExecuteAction::CreateStatic(&FClaudeCodeMenuExtender::OnOpenSettingsClicked))
);
}
void FClaudeCodeMenuExtender::OnOpenSettingsClicked()
{
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
if (SettingsModule)
{
SettingsModule->ShowViewer(TEXT("Project"), TEXT("Plugins"), TEXT("UnrealMCP"));
}
}
void FClaudeCodeMenuExtender::OnInitClaudeCodeClicked()
@@ -12,4 +12,5 @@ public:
private:
static void BuildMenu(UToolMenu* Menu);
static void OnInitClaudeCodeClicked();
static void OnOpenSettingsClicked();
};
@@ -18,6 +18,8 @@ public class UnrealMCPSetup : ModuleRules
"ToolMenus",
"Projects",
"Json",
"Settings", // ISettingsModule — open Project Settings programmatically
"UnrealMCP", // UUnrealMCPSettings — read configured port
});
}
}