adding "init claude code" fonctionality

This commit is contained in:
ange committed 2026-04-26 17:30:10 +02:00
1 parent 291030de43
commit 219c1ab7b0
25 files changed
+1041 -421

No files matched your search

+12 -2
View File
@@ -147,5 +147,15 @@ J'aimerais automatiser le processus à la création d'un projet unreal en ajouta
reprenons avec ce que nous savons :
Hier dans l'aprés-midi, nous avons, avec l'ancienne organization de dossier (cloné depuis https://github.com/chongdashu/unreal-mcp), réussi à faire fonctionner le server mcp. maintenant que nous avons modifié cette suructure, nous n'arrivons claude code ne lance plus le server mcp. L'ancienne structure se trouve ici : C:\UE_5.7\unreal-mcp\claude-unreal-mcp. le lock.
l'objectif de cette session de travail est de modifier le code du plugin afin que l'utilisateur puisse dans unreal initialiser le projet unreal complet pour être prêt à utiliser claude code. Donc il faut créer en c++ le bouton "init claude code" dont on a parlé hier. Je veux que le code soit bien découpé (pas tous dans un seul fichier). Nous pouvons même créer dans le plugin un nouveau module pour ces fonctionnalités.
Les étapes utilisateur souhaités:
- clone du repo dans le dossier plugin du projet unreal cible.
- l'utilisateur lance unreal (le plugin compile automatiquement)
- Un menu "Claude code" dans la menu bar est présent maintenant dans l'interface unreal.
- Un bouton "Init Claude Code" avec l'infobulle "Initialisation de claude code pour ce projet" est présent dans ce menu. Il permet à l'utilisateur d'initialiser le projet pour l'utilisation de Claude Code.
- Quand l'utilisateur clique ce bouton, une fenêtre de validation s'affiche pour demander : "étes-vous sûr de vouloir initialiser claude code pour ce projet ?". A noter que claude code ne peux être utilisé qu'avec des projet c++. Si ce n'est pas le cas, afficher un message "Claude Code ne peut être initialisé que dans un projet cpp".
- si l'utilisateur valide l'initialisation, le plugin copie le dossier .claude, ainsi que le .mcp.json à la racine du projet unreal en adaptant les chemins en relatif.
- si l'utilisateur ne valide pas. On ne fait rien.
- Le plugin initialise uv et attend que le venv soit prés avant de continuer.
- Quand tout est fini, il affiche une fenêtre (même en cas d'echec) recapitulant les modifications apportées et le status de l'instalattion (si tout c'est bien passé ou si il y a eu un/des problèmes).
Peux tu faire ce travail ?
-59
View File
@@ -1,59 +0,0 @@
2026-04-24 09:21:44,597 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP'
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest
2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest
2026-04-24 09:21:44,614 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-24 09:21:44,626 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-24 09:21:44,649 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-24 09:21:44,650 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-24 09:21:44,657 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-24 09:21:44,659 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-24 09:21:44,666 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-24 09:21:44,668 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-24 09:21:44,668 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-24 09:21:44,669 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-24 09:21:44,669 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-24 09:21:44,685 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-04-24 09:21:44,685 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: <mcp.shared.session.RequestResponder object at 0x000001F81A508440>
2026-04-24 09:21:44,686 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type ListToolsRequest
2026-04-24 09:21:44,686 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type ListToolsRequest
2026-04-24 09:21:44,686 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent
2026-04-24 09:21:44,687 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: <mcp.shared.session.RequestResponder object at 0x000001F81A1B2BA0>
2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type ListPromptsRequest
2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type ListPromptsRequest
2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent
2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: <mcp.shared.session.RequestResponder object at 0x000001F81A45D820>
2026-04-24 09:21:44,689 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type ListResourcesRequest
2026-04-24 09:21:44,689 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type ListResourcesRequest
2026-04-24 09:21:44,689 - mcp.server.fastmcp.resources.resource_manager - DEBUG - [resource_manager.py:89] - Listing resources
2026-04-24 09:21:44,689 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent
2026-04-24 09:21:59,189 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: <mcp.shared.session.RequestResponder object at 0x000001F81A583050>
2026-04-24 09:21:59,189 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type CallToolRequest
2026-04-24 09:21:59,189 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type CallToolRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP'
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest
2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest
2026-04-24 09:21:59,198 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-24 09:21:59,204 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-24 09:21:59,213 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-24 09:21:59,214 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-24 09:21:59,220 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-24 09:21:59,220 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:151] - Sending command: {"type": "get_actors_in_level", "params": {}}
2026-04-24 09:21:59,377 - UnrealMCP - INFO - [unreal_mcp_server.py:101] - Received complete response (3465 bytes)
2026-04-24 09:21:59,377 - UnrealMCP - INFO - [unreal_mcp_server.py:159] - Complete response from Unreal: {'status': 'success', 'result': {'actors': [{'name': 'WorldSettings_1', 'class': 'WorldSettings', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'Brush_1', 'class': 'Brush', 'location': [-90, 110, 20], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'DirectionalLight_0', 'class': 'DirectionalLight', 'location': [0, 0, 400], 'rotation': [-49.51711035415601, -10.308600787602971, 112.36067200001975], 'scale': [1, 1.0000000149011612, 1.0000000149011612]}, {'name': 'SkyAtmosphere_0', 'class': 'SkyAtmosphere', 'location': [0, 0, -6000], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'SkyLight_0', 'class': 'SkyLight', 'location': [0, 0, 600], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ExponentialHeightFog_0', 'class': 'ExponentialHeightFog', 'location': [-5600, -50, -6850], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'VolumetricCloud_0', 'class': 'VolumetricCloud', 'location': [0, 0, 700], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'PlayerStart_0', 'class': 'PlayerStart', 'location': [0, 0, 92], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'StaticMeshActor_0', 'class': 'StaticMeshActor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [400, 400, 400]}, {'name': 'Floor_0', 'class': 'StaticMeshActor', 'location': [0, 0, -0.5], 'rotation': [0, 0, 0], 'scale': [8, 8, 8]}, {'name': 'DefaultPhysicsVolume_0', 'class': 'DefaultPhysicsVolume', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'GameplayDebuggerPlayerManager_0', 'class': 'GameplayDebuggerPlayerManager', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ChaosDebugDrawActor', 'class': 'Actor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'AbstractNavData-Default', 'class': 'AbstractNavData', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}]}}
2026-04-24 09:21:59,377 - UnrealMCP - INFO - [editor_tools.py:35] - Complete response from Unreal: {'status': 'success', 'result': {'actors': [{'name': 'WorldSettings_1', 'class': 'WorldSettings', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'Brush_1', 'class': 'Brush', 'location': [-90, 110, 20], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'DirectionalLight_0', 'class': 'DirectionalLight', 'location': [0, 0, 400], 'rotation': [-49.51711035415601, -10.308600787602971, 112.36067200001975], 'scale': [1, 1.0000000149011612, 1.0000000149011612]}, {'name': 'SkyAtmosphere_0', 'class': 'SkyAtmosphere', 'location': [0, 0, -6000], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'SkyLight_0', 'class': 'SkyLight', 'location': [0, 0, 600], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ExponentialHeightFog_0', 'class': 'ExponentialHeightFog', 'location': [-5600, -50, -6850], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'VolumetricCloud_0', 'class': 'VolumetricCloud', 'location': [0, 0, 700], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'PlayerStart_0', 'class': 'PlayerStart', 'location': [0, 0, 92], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'StaticMeshActor_0', 'class': 'StaticMeshActor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [400, 400, 400]}, {'name': 'Floor_0', 'class': 'StaticMeshActor', 'location': [0, 0, -0.5], 'rotation': [0, 0, 0], 'scale': [8, 8, 8]}, {'name': 'DefaultPhysicsVolume_0', 'class': 'DefaultPhysicsVolume', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'GameplayDebuggerPlayerManager_0', 'class': 'GameplayDebuggerPlayerManager', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ChaosDebugDrawActor', 'class': 'Actor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'AbstractNavData-Default', 'class': 'AbstractNavData', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}]}}
2026-04-24 09:21:59,377 - UnrealMCP - INFO - [editor_tools.py:40] - Found 14 actors in level
2026-04-24 09:21:59,378 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent
-194
View File
@@ -1,194 +0,0 @@
2026-04-23 19:18:15,190 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP'
2026-04-23 19:18:15,190 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest
2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest
2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest
2026-04-23 19:18:15,195 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest
2026-04-23 19:18:15,195 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest
2026-04-23 19:18:15,216 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 19:18:15,222 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 19:18:15,230 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 19:18:15,231 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 19:18:15,237 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 19:18:15,237 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 19:18:15,254 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 19:18:15,256 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 19:18:15,256 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 19:18:17,259 - UnrealMCP - ERROR - [unreal_mcp_server.py:67] - Failed to connect to Unreal: [WinError 10061] Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée
2026-04-23 19:18:17,259 - UnrealMCP - WARNING - [unreal_mcp_server.py:213] - Could not connect to Unreal Engine
2026-04-23 19:18:17,259 - UnrealMCP - WARNING - [unreal_mcp_server.py:248] - Could not connect to Unreal Engine on startup
2026-04-23 19:18:17,261 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 19:28:11,152 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP'
2026-04-23 19:28:11,152 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-04-23 19:28:11,152 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest
2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest
2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest
2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest
2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest
2026-04-23 19:28:11,162 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 19:28:11,168 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 19:28:11,174 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 19:28:11,175 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 19:28:11,181 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 19:28:11,181 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 19:28:11,184 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 19:28:11,185 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 19:28:11,185 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 19:28:11,196 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 19:28:11,196 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 19:28:11,197 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 20:43:13,141 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP'
2026-04-23 20:43:13,141 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest
2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest
2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest
2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest
2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest
2026-04-23 20:43:13,163 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 20:43:13,176 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 20:43:13,189 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 20:43:13,191 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 20:43:13,204 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 20:44:41,613 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP'
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest
2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest
2026-04-23 20:44:41,634 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 20:44:41,650 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 20:44:41,667 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 20:44:41,668 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 20:44:41,682 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 20:44:41,683 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 20:44:41,689 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 20:44:41,692 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 20:44:41,692 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 20:44:41,693 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 20:44:41,693 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 20:44:41,695 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 21:49:21,797 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP'
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest
2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest
2026-04-23 21:49:21,815 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 21:49:21,830 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 21:49:21,844 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 21:49:21,846 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 21:49:21,858 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 21:49:21,859 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 21:49:21,865 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 21:49:21,870 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 21:52:38,233 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP'
2026-04-23 21:52:38,233 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest
2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest
2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest
2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest
2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest
2026-04-23 21:52:38,251 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 21:52:38,263 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 21:52:38,278 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 21:52:38,279 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 21:52:38,291 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 21:52:38,292 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 21:52:38,296 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 21:52:38,299 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 21:52:38,299 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 21:52:38,300 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 21:52:38,300 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 21:52:40,398 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP'
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest
2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest
2026-04-23 22:06:00,175 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 22:06:00,183 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 22:06:00,192 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 22:06:00,193 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 22:06:00,213 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 22:06:00,215 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 22:06:00,219 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 22:06:00,220 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 22:06:00,220 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 22:06:00,221 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 22:06:00,221 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 22:06:02,502 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP'
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest
2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest
2026-04-23 22:12:35,788 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest
2026-04-23 22:12:35,800 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 22:12:35,807 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 22:12:35,830 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 22:12:35,831 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 22:12:35,839 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 22:12:35,840 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 22:12:35,844 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 22:12:35,846 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 22:12:35,846 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 22:12:35,847 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 22:12:35,847 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 22:12:35,849 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP'
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest
2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest
2026-04-23 22:15:02,976 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 22:15:02,985 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 22:15:03,006 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 22:15:03,007 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 22:15:03,014 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 22:15:03,015 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 22:15:03,020 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 22:15:03,022 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 22:15:03,023 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 22:15:03,023 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 22:15:03,023 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 22:15:03,025 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
2026-04-23 22:18:00,708 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP'
2026-04-23 22:18:00,708 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest
2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest
2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest
2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest
2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest
2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest
2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest
2026-04-23 22:18:00,720 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully
2026-04-23 22:18:00,729 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully
2026-04-23 22:18:00,749 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully
2026-04-23 22:18:00,752 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully
2026-04-23 22:18:00,760 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully
2026-04-23 22:18:00,760 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport
2026-04-23 22:18:00,763 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-04-23 22:18:00,766 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up
2026-04-23 22:18:00,766 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557...
2026-04-23 22:18:00,778 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine
2026-04-23 22:18:00,778 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup
2026-04-23 22:18:00,782 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down
@@ -1,4 +1,5 @@
#include "Commands/UnrealMCPBlueprintCommands.h"
#include "UnrealMCPLog.h"
#include "Commands/UnrealMCPCommonUtils.h"
#include "Engine/Blueprint.h"
#include "Engine/BlueprintGeneratedClass.h"
@@ -130,11 +131,11 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCreateBlueprint(const
if (FoundClass)
{
SelectedParentClass = FoundClass;
UE_LOG(LogTemp, Log, TEXT("Successfully set parent class to '%s'"), *ClassName);
UE_LOG(LogUnrealMCP, Log, TEXT("Successfully set parent class to '%s'"), *ClassName);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Could not find specified parent class '%s' at paths: /Script/Engine.%s or /Script/Game.%s, defaulting to AActor"),
UE_LOG(LogUnrealMCP, Warning, TEXT("Could not find specified parent class '%s' at paths: /Script/Engine.%s or /Script/Game.%s, defaulting to AActor"),
*ClassName, *ClassName, *ClassName);
}
}
@@ -283,7 +284,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
}
// Log all input parameters for debugging
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Blueprint: %s, Component: %s, Property: %s"),
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Blueprint: %s, Component: %s, Property: %s"),
*BlueprintName, *ComponentName, *PropertyName);
// Log property_value if available
@@ -302,34 +303,34 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
default: ValueType = TEXT("Unknown"); break;
}
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Value Type: %s"), *ValueType);
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Value Type: %s"), *ValueType);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - No property_value provided"));
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - No property_value provided"));
}
// Find the blueprint
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
if (!Blueprint)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Blueprint not found: %s"), *BlueprintName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Blueprint not found: %s"), *BlueprintName);
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Blueprint found: %s (Class: %s)"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Blueprint found: %s (Class: %s)"),
*BlueprintName,
Blueprint->GeneratedClass ? *Blueprint->GeneratedClass->GetName() : TEXT("NULL"));
}
// Find the component
USCS_Node* ComponentNode = nullptr;
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Searching for component %s in blueprint nodes"), *ComponentName);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Searching for component %s in blueprint nodes"), *ComponentName);
if (!Blueprint->SimpleConstructionScript)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - SimpleConstructionScript is NULL for blueprint %s"), *BlueprintName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - SimpleConstructionScript is NULL for blueprint %s"), *BlueprintName);
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid blueprint construction script"));
}
@@ -337,7 +338,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
{
if (Node)
{
UE_LOG(LogTemp, Verbose, TEXT("SetComponentProperty - Found node: %s"), *Node->GetVariableName().ToString());
UE_LOG(LogUnrealMCP, Verbose, TEXT("SetComponentProperty - Found node: %s"), *Node->GetVariableName().ToString());
if (Node->GetVariableName().ToString() == ComponentName)
{
ComponentNode = Node;
@@ -346,18 +347,18 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
}
else
{
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Found NULL node in blueprint"));
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Found NULL node in blueprint"));
}
}
if (!ComponentNode)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component not found: %s"), *ComponentName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Component not found: %s"), *ComponentName);
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Component found: %s (Class: %s)"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Component found: %s (Class: %s)"),
*ComponentName,
ComponentNode->ComponentTemplate ? *ComponentNode->ComponentTemplate->GetClass()->GetName() : TEXT("NULL"));
}
@@ -366,22 +367,22 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
UObject* ComponentTemplate = ComponentNode->ComponentTemplate;
if (!ComponentTemplate)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component template is NULL for %s"), *ComponentName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Component template is NULL for %s"), *ComponentName);
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid component template"));
}
// Check if this is a Spring Arm component and log special debug info
if (ComponentTemplate->GetClass()->GetName().Contains(TEXT("SpringArm")))
{
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm component detected! Class: %s"),
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - SpringArm component detected! Class: %s"),
*ComponentTemplate->GetClass()->GetPathName());
// Log all properties of the SpringArm component class
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm properties:"));
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - SpringArm properties:"));
for (TFieldIterator<FProperty> PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt)
{
FProperty* Prop = *PropIt;
UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
}
// Special handling for Spring Arm properties
@@ -393,7 +394,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
FProperty* Property = FindFProperty<FProperty>(ComponentTemplate->GetClass(), *PropertyName);
if (!Property)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on SpringArm component"), *PropertyName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Property %s not found on SpringArm component"), *PropertyName);
return FUnrealMCPCommonUtils::CreateErrorResponse(
FString::Printf(TEXT("Property %s not found on SpringArm component"), *PropertyName));
}
@@ -427,7 +428,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
if (JsonValue->Type == EJson::Number)
{
const float Value = JsonValue->AsNumber();
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting float property %s to %f"), *PropertyName, Value);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting float property %s to %f"), *PropertyName, Value);
FloatProp->SetPropertyValue_InContainer(ComponentTemplate, Value);
bSuccess = true;
}
@@ -437,14 +438,14 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
if (JsonValue->Type == EJson::Boolean)
{
const bool Value = JsonValue->AsBool();
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting bool property %s to %d"), *PropertyName, Value);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting bool property %s to %d"), *PropertyName, Value);
BoolProp->SetPropertyValue_InContainer(ComponentTemplate, Value);
bSuccess = true;
}
}
else if (FStructProperty* StructProp = CastField<FStructProperty>(Property))
{
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Handling struct property %s of type %s"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Handling struct property %s of type %s"),
*PropertyName, *StructProp->Struct->GetName());
// Special handling for common Spring Arm struct properties
@@ -489,7 +490,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
if (bSuccess)
{
// Mark the blueprint as modified
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set SpringArm property %s"), *PropertyName);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Successfully set SpringArm property %s"), *PropertyName);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
@@ -500,7 +501,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
}
else
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set SpringArm property %s"), *PropertyName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set SpringArm property %s"), *PropertyName);
return FUnrealMCPCommonUtils::CreateErrorResponse(
FString::Printf(TEXT("Failed to set SpringArm property %s"), *PropertyName));
}
@@ -518,15 +519,15 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
FProperty* Property = FindFProperty<FProperty>(ComponentTemplate->GetClass(), *PropertyName);
if (!Property)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on component %s"),
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Property %s not found on component %s"),
*PropertyName, *ComponentName);
// List all available properties for this component
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available properties for %s:"), *ComponentName);
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Available properties for %s:"), *ComponentName);
for (TFieldIterator<FProperty> PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt)
{
FProperty* Prop = *PropIt;
UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
}
return FUnrealMCPCommonUtils::CreateErrorResponse(
@@ -534,7 +535,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property found: %s (Type: %s)"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property found: %s (Type: %s)"),
*PropertyName, *Property->GetCPPType());
}
@@ -542,7 +543,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
FString ErrorMessage;
// Handle different property types
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Attempting to set property %s"), *PropertyName);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Attempting to set property %s"), *PropertyName);
// Add try-catch block to catch and log any crashes
try
@@ -550,7 +551,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
if (FStructProperty* StructProp = CastField<FStructProperty>(Property))
{
// Handle vector properties
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is a struct: %s"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property is a struct: %s"),
StructProp->Struct ? *StructProp->Struct->GetName() : TEXT("NULL"));
if (StructProp->Struct == TBaseStructure<FVector>::Get())
@@ -567,7 +568,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
Arr[2]->AsNumber()
);
void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f)"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f)"),
Vec.X, Vec.Y, Vec.Z);
StructProp->CopySingleValue(PropertyAddr, &Vec);
bSuccess = true;
@@ -575,7 +576,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
else
{
ErrorMessage = FString::Printf(TEXT("Vector property requires 3 values, got %d"), Arr.Num());
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
}
}
else if (JsonValue->Type == EJson::Number)
@@ -584,7 +585,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
float Value = JsonValue->AsNumber();
FVector Vec(Value, Value, Value);
void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f) from scalar"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f) from scalar"),
Vec.X, Vec.Y, Vec.Z);
StructProp->CopySingleValue(PropertyAddr, &Vec);
bSuccess = true;
@@ -592,30 +593,30 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
else
{
ErrorMessage = TEXT("Vector property requires either a single number or array of 3 numbers");
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
}
}
else
{
// Handle other struct properties using default handler
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic struct handler for %s"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Using generic struct handler for %s"),
*PropertyName);
bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage);
if (!bSuccess)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set struct property: %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set struct property: %s"), *ErrorMessage);
}
}
}
else if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Property))
{
// Handle enum properties
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is an enum"));
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property is an enum"));
if (JsonValue->Type == EJson::String)
{
FString EnumValueName = JsonValue->AsString();
UEnum* Enum = EnumProp->GetEnum();
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from string: %s"), *EnumValueName);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting enum from string: %s"), *EnumValueName);
if (Enum)
{
@@ -623,7 +624,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
if (EnumValue != INDEX_NONE)
{
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Found enum value: %lld"), EnumValue);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Found enum value: %lld"), EnumValue);
EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(
ComponentTemplate,
EnumValue
@@ -633,31 +634,31 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
else
{
// List all possible enum values
UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available enum values for %s:"),
UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Available enum values for %s:"),
*Enum->GetName());
for (int32 i = 0; i < Enum->NumEnums(); i++)
{
UE_LOG(LogTemp, Warning, TEXT(" - %s (%lld)"),
UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (%lld)"),
*Enum->GetNameStringByIndex(i),
Enum->GetValueByIndex(i));
}
ErrorMessage = FString::Printf(TEXT("Invalid enum value '%s' for property %s"),
*EnumValueName, *PropertyName);
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
}
}
else
{
ErrorMessage = TEXT("Enum object is NULL");
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
}
}
else if (JsonValue->Type == EJson::Number)
{
// Allow setting enum by integer value
int64 EnumValue = JsonValue->AsNumber();
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from number: %lld"), EnumValue);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting enum from number: %lld"), EnumValue);
EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(
ComponentTemplate,
EnumValue
@@ -667,60 +668,60 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
else
{
ErrorMessage = TEXT("Enum property requires either a string name or integer value");
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
}
}
else if (FNumericProperty* NumericProp = CastField<FNumericProperty>(Property))
{
// Handle numeric properties
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is numeric: IsInteger=%d, IsFloat=%d"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property is numeric: IsInteger=%d, IsFloat=%d"),
NumericProp->IsInteger(), NumericProp->IsFloatingPoint());
if (JsonValue->Type == EJson::Number)
{
double Value = JsonValue->AsNumber();
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting numeric value: %f"), Value);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting numeric value: %f"), Value);
if (NumericProp->IsInteger())
{
NumericProp->SetIntPropertyValue(ComponentTemplate, (int64)Value);
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set integer value: %lld"), (int64)Value);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Set integer value: %lld"), (int64)Value);
bSuccess = true;
}
else if (NumericProp->IsFloatingPoint())
{
NumericProp->SetFloatingPointPropertyValue(ComponentTemplate, Value);
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set float value: %f"), Value);
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Set float value: %f"), Value);
bSuccess = true;
}
}
else
{
ErrorMessage = TEXT("Numeric property requires a number value");
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
}
}
else
{
// Handle all other property types using default handler
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic property handler for %s (Type: %s)"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Using generic property handler for %s (Type: %s)"),
*PropertyName, *Property->GetCPPType());
bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage);
if (!bSuccess)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property: %s"), *ErrorMessage);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set property: %s"), *ErrorMessage);
}
}
}
catch (const std::exception& Ex)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - EXCEPTION: %s"), ANSI_TO_TCHAR(Ex.what()));
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - EXCEPTION: %s"), ANSI_TO_TCHAR(Ex.what()));
return FUnrealMCPCommonUtils::CreateErrorResponse(
FString::Printf(TEXT("Exception while setting property %s: %s"), *PropertyName, ANSI_TO_TCHAR(Ex.what())));
}
catch (...)
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - UNKNOWN EXCEPTION occurred while setting property %s"), *PropertyName);
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - UNKNOWN EXCEPTION occurred while setting property %s"), *PropertyName);
return FUnrealMCPCommonUtils::CreateErrorResponse(
FString::Printf(TEXT("Unknown exception while setting property %s"), *PropertyName));
}
@@ -728,7 +729,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
if (bSuccess)
{
// Mark the blueprint as modified
UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set property %s on component %s"),
UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Successfully set property %s on component %s"),
*PropertyName, *ComponentName);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
@@ -740,13 +741,13 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(
}
else
{
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property %s: %s"),
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set property %s: %s"),
*PropertyName, *ErrorMessage);
return FUnrealMCPCommonUtils::CreateErrorResponse(ErrorMessage);
}
}
UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Missing 'property_value' parameter"));
UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Missing 'property_value' parameter"));
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_value' parameter"));
}
@@ -805,7 +806,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetPhysicsProperties(
float Mass = Params->GetNumberField(TEXT("mass"));
// In UE5.5, use proper overrideMass instead of just scaling
PrimComponent->SetMassOverrideInKg(NAME_None, Mass);
UE_LOG(LogTemp, Display, TEXT("Set mass for component %s to %f kg"), *ComponentName, Mass);
UE_LOG(LogUnrealMCP, Display, TEXT("Set mass for component %s to %f kg"), *ComponentName, Mass);
}
if (Params->HasField(TEXT("linear_damping")))
@@ -1,4 +1,5 @@
#include "Commands/UnrealMCPBlueprintNodeCommands.h"
#include "UnrealMCPLog.h"
#include "Commands/UnrealMCPCommonUtils.h"
#include "Engine/Blueprint.h"
#include "Engine/BlueprintGeneratedClass.h"
@@ -17,9 +18,6 @@
#include "Kismet/GameplayStatics.h"
#include "EdGraphSchema_K2.h"
// Declare the log category
DEFINE_LOG_CATEGORY_STATIC(LogUnrealMCP, Log, All);
FUnrealMCPBlueprintNodeCommands::FUnrealMCPBlueprintNodeCommands()
{
}
@@ -310,7 +308,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
UK2Node_CallFunction* FunctionNode = nullptr;
// Add extensive logging for debugging
UE_LOG(LogTemp, Display, TEXT("Looking for function '%s' in target '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT("Looking for function '%s' in target '%s'"),
*FunctionName, Target.IsEmpty() ? TEXT("Blueprint") : *Target);
// Check if we have a target class specified
@@ -321,7 +319,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// First try without a prefix
TargetClass = FindFirstObject<UClass>(*Target);
UE_LOG(LogTemp, Display, TEXT("Tried to find class '%s': %s"),
UE_LOG(LogUnrealMCP, Display, TEXT("Tried to find class '%s': %s"),
*Target, TargetClass ? TEXT("Found") : TEXT("Not found"));
// If not found, try with U prefix (common convention for UE classes)
@@ -329,7 +327,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
{
FString TargetWithPrefix = FString(TEXT("U")) + Target;
TargetClass = FindFirstObject<UClass>(*TargetWithPrefix);
UE_LOG(LogTemp, Display, TEXT("Tried to find class '%s': %s"),
UE_LOG(LogUnrealMCP, Display, TEXT("Tried to find class '%s': %s"),
*TargetWithPrefix, TargetClass ? TEXT("Found") : TEXT("Not found"));
}
@@ -346,7 +344,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
TargetClass = FindFirstObject<UClass>(*ClassName);
if (TargetClass)
{
UE_LOG(LogTemp, Display, TEXT("Found class using alternative name '%s'"), *ClassName);
UE_LOG(LogUnrealMCP, Display, TEXT("Found class using alternative name '%s'"), *ClassName);
break;
}
}
@@ -361,7 +359,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
{
// Try loading it from its known package
TargetClass = LoadObject<UClass>(nullptr, TEXT("/Script/Engine.GameplayStatics"));
UE_LOG(LogTemp, Display, TEXT("Explicitly loading GameplayStatics: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT("Explicitly loading GameplayStatics: %s"),
TargetClass ? TEXT("Success") : TEXT("Failed"));
}
}
@@ -369,7 +367,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// If we found a target class, look for the function there
if (TargetClass)
{
UE_LOG(LogTemp, Display, TEXT("Looking for function '%s' in class '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT("Looking for function '%s' in class '%s'"),
*FunctionName, *TargetClass->GetName());
// First try exact name
@@ -379,7 +377,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
UClass* CurrentClass = TargetClass;
while (!Function && CurrentClass)
{
UE_LOG(LogTemp, Display, TEXT("Searching in class: %s"), *CurrentClass->GetName());
UE_LOG(LogUnrealMCP, Display, TEXT("Searching in class: %s"), *CurrentClass->GetName());
// Try exact match
Function = CurrentClass->FindFunctionByName(*FunctionName);
@@ -390,11 +388,11 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
for (TFieldIterator<UFunction> FuncIt(CurrentClass); FuncIt; ++FuncIt)
{
UFunction* AvailableFunc = *FuncIt;
UE_LOG(LogTemp, Display, TEXT(" - Available function: %s"), *AvailableFunc->GetName());
UE_LOG(LogUnrealMCP, Display, TEXT(" - Available function: %s"), *AvailableFunc->GetName());
if (AvailableFunc->GetName().Equals(FunctionName, ESearchCase::IgnoreCase))
{
UE_LOG(LogTemp, Display, TEXT(" - Found case-insensitive match: %s"), *AvailableFunc->GetName());
UE_LOG(LogUnrealMCP, Display, TEXT(" - Found case-insensitive match: %s"), *AvailableFunc->GetName());
Function = AvailableFunc;
break;
}
@@ -411,7 +409,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
if (TargetClass->GetName() == TEXT("GameplayStatics") &&
(FunctionName == TEXT("GetActorOfClass") || FunctionName.Equals(TEXT("GetActorOfClass"), ESearchCase::IgnoreCase)))
{
UE_LOG(LogTemp, Display, TEXT("Using special case handling for GameplayStatics::GetActorOfClass"));
UE_LOG(LogUnrealMCP, Display, TEXT("Using special case handling for GameplayStatics::GetActorOfClass"));
// Create the function node directly
FunctionNode = NewObject<UK2Node_CallFunction>(EventGraph);
@@ -430,12 +428,12 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
FunctionNode->PostPlacedNewNode();
FunctionNode->AllocateDefaultPins();
UE_LOG(LogTemp, Display, TEXT("Created GetActorOfClass node directly"));
UE_LOG(LogUnrealMCP, Display, TEXT("Created GetActorOfClass node directly"));
// List all pins
for (UEdGraphPin* Pin : FunctionNode->Pins)
{
UE_LOG(LogTemp, Display, TEXT(" - Pin: %s, Direction: %d, Category: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT(" - Pin: %s, Direction: %d, Category: %s"),
*Pin->PinName.ToString(), (int32)Pin->Direction, *Pin->PinType.PinCategory.ToString());
}
}
@@ -447,7 +445,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// If we still haven't found the function, try in the blueprint's class
if (!Function && !FunctionNode)
{
UE_LOG(LogTemp, Display, TEXT("Trying to find function in blueprint class"));
UE_LOG(LogUnrealMCP, Display, TEXT("Trying to find function in blueprint class"));
Function = Blueprint->GeneratedClass->FindFunctionByName(*FunctionName);
}
@@ -478,12 +476,12 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
UEdGraphPin* ParamPin = FUnrealMCPCommonUtils::FindPin(FunctionNode, ParamName, EGPD_Input);
if (ParamPin)
{
UE_LOG(LogTemp, Display, TEXT("Found parameter pin '%s' of category '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT("Found parameter pin '%s' of category '%s'"),
*ParamName, *ParamPin->PinType.PinCategory.ToString());
UE_LOG(LogTemp, Display, TEXT(" Current default value: '%s'"), *ParamPin->DefaultValue);
UE_LOG(LogUnrealMCP, Display, TEXT(" Current default value: '%s'"), *ParamPin->DefaultValue);
if (ParamPin->PinType.PinSubCategoryObject.IsValid())
{
UE_LOG(LogTemp, Display, TEXT(" Pin subcategory: '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Pin subcategory: '%s'"),
*ParamPin->PinType.PinSubCategoryObject->GetName());
}
@@ -491,7 +489,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
if (ParamValue->Type == EJson::String)
{
FString StringVal = ParamValue->AsString();
UE_LOG(LogTemp, Display, TEXT(" Setting string parameter '%s' to: '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Setting string parameter '%s' to: '%s'"),
*ParamName, *StringVal);
// Handle class reference parameters (e.g., ActorClass in GetActorOfClass)
@@ -547,7 +545,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// Ensure we're using an integer value (no decimal)
int32 IntValue = FMath::RoundToInt(ParamValue->AsNumber());
ParamPin->DefaultValue = FString::FromInt(IntValue);
UE_LOG(LogTemp, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"),
*ParamName, IntValue, *ParamPin->DefaultValue);
}
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
@@ -555,14 +553,14 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// For other numeric types
float FloatValue = ParamValue->AsNumber();
ParamPin->DefaultValue = FString::SanitizeFloat(FloatValue);
UE_LOG(LogTemp, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"),
*ParamName, FloatValue, *ParamPin->DefaultValue);
}
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Boolean)
{
bool BoolValue = ParamValue->AsBool();
ParamPin->DefaultValue = BoolValue ? TEXT("true") : TEXT("false");
UE_LOG(LogTemp, Display, TEXT(" Set boolean parameter '%s' to: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set boolean parameter '%s' to: %s"),
*ParamName, *ParamPin->DefaultValue);
}
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct && ParamPin->PinType.PinSubCategoryObject == TBaseStructure<FVector>::Get())
@@ -582,14 +580,14 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
FString VectorString = FString::Printf(TEXT("(X=%f,Y=%f,Z=%f)"), X, Y, Z);
ParamPin->DefaultValue = VectorString;
UE_LOG(LogTemp, Display, TEXT(" Set vector parameter '%s' to: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set vector parameter '%s' to: %s"),
*ParamName, *VectorString);
UE_LOG(LogTemp, Display, TEXT(" Final pin value: '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Final pin value: '%s'"),
*ParamPin->DefaultValue);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Array parameter type not fully supported yet"));
UE_LOG(LogUnrealMCP, Warning, TEXT("Array parameter type not fully supported yet"));
}
}
}
@@ -602,7 +600,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// Ensure we're using an integer value (no decimal)
int32 IntValue = FMath::RoundToInt(ParamValue->AsNumber());
ParamPin->DefaultValue = FString::FromInt(IntValue);
UE_LOG(LogTemp, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"),
*ParamName, IntValue, *ParamPin->DefaultValue);
}
else
@@ -610,7 +608,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
// For other numeric types
float FloatValue = ParamValue->AsNumber();
ParamPin->DefaultValue = FString::SanitizeFloat(FloatValue);
UE_LOG(LogTemp, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"),
*ParamName, FloatValue, *ParamPin->DefaultValue);
}
}
@@ -618,12 +616,12 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
{
bool BoolValue = ParamValue->AsBool();
ParamPin->DefaultValue = BoolValue ? TEXT("true") : TEXT("false");
UE_LOG(LogTemp, Display, TEXT(" Set boolean parameter '%s' to: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set boolean parameter '%s' to: %s"),
*ParamName, *ParamPin->DefaultValue);
}
else if (ParamValue->Type == EJson::Array)
{
UE_LOG(LogTemp, Display, TEXT(" Processing array parameter '%s'"), *ParamName);
UE_LOG(LogUnrealMCP, Display, TEXT(" Processing array parameter '%s'"), *ParamName);
// Handle array parameters - like Vector parameters
const TArray<TSharedPtr<FJsonValue>>* ArrayValue;
if (ParamValue->TryGetArray(ArrayValue))
@@ -641,14 +639,14 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
FString VectorString = FString::Printf(TEXT("(X=%f,Y=%f,Z=%f)"), X, Y, Z);
ParamPin->DefaultValue = VectorString;
UE_LOG(LogTemp, Display, TEXT(" Set vector parameter '%s' to: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Set vector parameter '%s' to: %s"),
*ParamName, *VectorString);
UE_LOG(LogTemp, Display, TEXT(" Final pin value: '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT(" Final pin value: '%s'"),
*ParamPin->DefaultValue);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Array parameter type not fully supported yet"));
UE_LOG(LogUnrealMCP, Warning, TEXT("Array parameter type not fully supported yet"));
}
}
}
@@ -656,7 +654,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Parameter pin '%s' not found"), *ParamName);
UE_LOG(LogUnrealMCP, Warning, TEXT("Parameter pin '%s' not found"), *ParamName);
}
}
}
@@ -910,7 +908,7 @@ TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleFindBlueprintNode
UK2Node_Event* EventNode = Cast<UK2Node_Event>(Node);
if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName))
{
UE_LOG(LogTemp, Display, TEXT("Found event node with name %s: %s"), *EventName, *EventNode->NodeGuid.ToString());
UE_LOG(LogUnrealMCP, Display, TEXT("Found event node with name %s: %s"), *EventName, *EventNode->NodeGuid.ToString());
NodeGuidArray.Add(MakeShared<FJsonValueString>(EventNode->NodeGuid.ToString()));
}
}
@@ -1,4 +1,5 @@
#include "Commands/UnrealMCPCommonUtils.h"
#include "UnrealMCPLog.h"
#include "GameFramework/Actor.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
@@ -199,7 +200,7 @@ UK2Node_Event* FUnrealMCPCommonUtils::CreateEventNode(UEdGraph* Graph, const FSt
UK2Node_Event* EventNode = Cast<UK2Node_Event>(Node);
if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName))
{
UE_LOG(LogTemp, Display, TEXT("Using existing event node with name %s (ID: %s)"),
UE_LOG(LogUnrealMCP, Display, TEXT("Using existing event node with name %s (ID: %s)"),
*EventName, *EventNode->NodeGuid.ToString());
return EventNode;
}
@@ -221,12 +222,12 @@ UK2Node_Event* FUnrealMCPCommonUtils::CreateEventNode(UEdGraph* Graph, const FSt
Graph->AddNode(EventNode, true);
EventNode->PostPlacedNewNode();
EventNode->AllocateDefaultPins();
UE_LOG(LogTemp, Display, TEXT("Created new event node with name %s (ID: %s)"),
UE_LOG(LogUnrealMCP, Display, TEXT("Created new event node with name %s (ID: %s)"),
*EventName, *EventNode->NodeGuid.ToString());
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to find function for event name: %s"), *EventName);
UE_LOG(LogUnrealMCP, Error, TEXT("Failed to find function for event name: %s"), *EventName);
}
return EventNode;
@@ -370,12 +371,12 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P
}
// Log all pins for debugging
UE_LOG(LogTemp, Display, TEXT("FindPin: Looking for pin '%s' (Direction: %d) in node '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT("FindPin: Looking for pin '%s' (Direction: %d) in node '%s'"),
*PinName, (int32)Direction, *Node->GetName());
for (UEdGraphPin* Pin : Node->Pins)
{
UE_LOG(LogTemp, Display, TEXT(" - Available pin: '%s', Direction: %d, Category: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT(" - Available pin: '%s', Direction: %d, Category: %s"),
*Pin->PinName.ToString(), (int32)Pin->Direction, *Pin->PinType.PinCategory.ToString());
}
@@ -384,7 +385,7 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P
{
if (Pin->PinName.ToString() == PinName && (Direction == EGPD_MAX || Pin->Direction == Direction))
{
UE_LOG(LogTemp, Display, TEXT(" - Found exact matching pin: '%s'"), *Pin->PinName.ToString());
UE_LOG(LogUnrealMCP, Display, TEXT(" - Found exact matching pin: '%s'"), *Pin->PinName.ToString());
return Pin;
}
}
@@ -395,7 +396,7 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P
if (Pin->PinName.ToString().Equals(PinName, ESearchCase::IgnoreCase) &&
(Direction == EGPD_MAX || Pin->Direction == Direction))
{
UE_LOG(LogTemp, Display, TEXT(" - Found case-insensitive matching pin: '%s'"), *Pin->PinName.ToString());
UE_LOG(LogUnrealMCP, Display, TEXT(" - Found case-insensitive matching pin: '%s'"), *Pin->PinName.ToString());
return Pin;
}
}
@@ -407,13 +408,13 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P
{
if (Pin->Direction == EGPD_Output && Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec)
{
UE_LOG(LogTemp, Display, TEXT(" - Found fallback data output pin: '%s'"), *Pin->PinName.ToString());
UE_LOG(LogUnrealMCP, Display, TEXT(" - Found fallback data output pin: '%s'"), *Pin->PinName.ToString());
return Pin;
}
}
}
UE_LOG(LogTemp, Warning, TEXT(" - No matching pin found for '%s'"), *PinName);
UE_LOG(LogUnrealMCP, Warning, TEXT(" - No matching pin found for '%s'"), *PinName);
return nullptr;
}
@@ -501,7 +502,7 @@ UK2Node_Event* FUnrealMCPCommonUtils::FindExistingEventNode(UEdGraph* Graph, con
UK2Node_Event* EventNode = Cast<UK2Node_Event>(Node);
if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName))
{
UE_LOG(LogTemp, Display, TEXT("Found existing event node with name: %s"), *EventName);
UE_LOG(LogUnrealMCP, Display, TEXT("Found existing event node with name: %s"), *EventName);
return EventNode;
}
}
@@ -567,7 +568,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr
uint8 ByteValue = static_cast<uint8>(Value->AsNumber());
ByteProp->SetPropertyValue(PropertyAddr, ByteValue);
UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric value: %d"),
UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric value: %d"),
*PropertyName, ByteValue);
return true;
}
@@ -582,7 +583,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr
uint8 ByteValue = FCString::Atoi(*EnumValueName);
ByteProp->SetPropertyValue(PropertyAddr, ByteValue);
UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric string value: %s -> %d"),
UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric string value: %s -> %d"),
*PropertyName, *EnumValueName, ByteValue);
return true;
}
@@ -604,17 +605,17 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr
{
ByteProp->SetPropertyValue(PropertyAddr, static_cast<uint8>(EnumValue));
UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to name value: %s -> %lld"),
UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to name value: %s -> %lld"),
*PropertyName, *EnumValueName, EnumValue);
return true;
}
else
{
// Log all possible enum values for debugging
UE_LOG(LogTemp, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName);
UE_LOG(LogUnrealMCP, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName);
for (int32 i = 0; i < EnumDef->NumEnums(); i++)
{
UE_LOG(LogTemp, Warning, TEXT(" - %s (value: %d)"),
UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (value: %d)"),
*EnumDef->GetNameStringByIndex(i), EnumDef->GetValueByIndex(i));
}
@@ -645,7 +646,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr
int64 EnumValue = static_cast<int64>(Value->AsNumber());
UnderlyingNumericProp->SetIntPropertyValue(PropertyAddr, EnumValue);
UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric value: %lld"),
UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric value: %lld"),
*PropertyName, EnumValue);
return true;
}
@@ -660,7 +661,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr
int64 EnumValue = FCString::Atoi64(*EnumValueName);
UnderlyingNumericProp->SetIntPropertyValue(PropertyAddr, EnumValue);
UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric string value: %s -> %lld"),
UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric string value: %s -> %lld"),
*PropertyName, *EnumValueName, EnumValue);
return true;
}
@@ -682,17 +683,17 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr
{
UnderlyingNumericProp->SetIntPropertyValue(PropertyAddr, EnumValue);
UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to name value: %s -> %lld"),
UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to name value: %s -> %lld"),
*PropertyName, *EnumValueName, EnumValue);
return true;
}
else
{
// Log all possible enum values for debugging
UE_LOG(LogTemp, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName);
UE_LOG(LogUnrealMCP, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName);
for (int32 i = 0; i < EnumDef->NumEnums(); i++)
{
UE_LOG(LogTemp, Warning, TEXT(" - %s (value: %d)"),
UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (value: %d)"),
*EnumDef->GetNameStringByIndex(i), EnumDef->GetValueByIndex(i));
}
@@ -1,4 +1,5 @@
#include "Commands/UnrealMCPEditorCommands.h"
#include "UnrealMCPLog.h"
#include "Commands/UnrealMCPCommonUtils.h"
#include "Editor.h"
#include "EditorViewportClient.h"
@@ -40,7 +41,7 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleCommand(const FString& C
{
if (CommandType == TEXT("create_actor"))
{
UE_LOG(LogTemp, Warning, TEXT("'create_actor' command is deprecated and will be removed in a future version. Please use 'spawn_actor' instead."));
UE_LOG(LogUnrealMCP, Warning, TEXT("'create_actor' command is deprecated and will be removed in a future version. Please use 'spawn_actor' instead."));
}
return HandleSpawnActor(Params);
}
@@ -1,4 +1,5 @@
#include "Commands/UnrealMCPProjectCommands.h"
#include "UnrealMCPLog.h"
#include "Commands/UnrealMCPCommonUtils.h"
#include "GameFramework/InputSettings.h"
@@ -1,4 +1,5 @@
#include "Commands/UnrealMCPUMGCommands.h"
#include "UnrealMCPLog.h"
#include "Commands/UnrealMCPCommonUtils.h"
#include "Editor.h"
#include "EditorAssetLibrary.h"
+41 -40
View File
@@ -1,4 +1,5 @@
#include "MCPServerRunnable.h"
#include "UnrealMCPLog.h"
#include "UnrealMCPBridge.h"
#include "Sockets.h"
#include "SocketSubsystem.h"
@@ -19,7 +20,7 @@ FMCPServerRunnable::FMCPServerRunnable(UUnrealMCPBridge* InBridge, TSharedPtr<FS
, ListenerSocket(InListenerSocket)
, bRunning(true)
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Created server runnable"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Created server runnable"));
}
FMCPServerRunnable::~FMCPServerRunnable()
@@ -34,21 +35,21 @@ bool FMCPServerRunnable::Init()
uint32 FMCPServerRunnable::Run()
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Server thread starting..."));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Server thread starting..."));
while (bRunning)
{
// UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Waiting for client connection..."));
// UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Waiting for client connection..."));
bool bPending = false;
if (ListenerSocket->HasPendingConnection(bPending) && bPending)
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Client connection pending, accepting..."));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Client connection pending, accepting..."));
ClientSocket = MakeShareable(ListenerSocket->Accept(TEXT("MCPClient")));
if (ClientSocket.IsValid())
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Client connection accepted"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Client connection accepted"));
// Set socket options to improve connection stability
ClientSocket->SetNoDelay(true);
@@ -64,14 +65,14 @@ uint32 FMCPServerRunnable::Run()
{
if (BytesRead == 0)
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Client disconnected (zero bytes)"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Client disconnected (zero bytes)"));
break;
}
// Convert received data to string
Buffer[BytesRead] = '\0';
FString ReceivedText = UTF8_TO_TCHAR(Buffer);
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Received: %s"), *ReceivedText);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Received: %s"), *ReceivedText);
// Parse JSON
TSharedPtr<FJsonObject> JsonObject;
@@ -87,26 +88,26 @@ uint32 FMCPServerRunnable::Run()
FString Response = Bridge->ExecuteCommand(CommandType, JsonObject->GetObjectField(TEXT("params")));
// Log response for debugging
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response);
// Send response
int32 BytesSent = 0;
if (!ClientSocket->Send((uint8*)TCHAR_TO_UTF8(*Response), Response.Len(), BytesSent))
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to send response"));
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to send response"));
}
else {
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Response sent successfully, bytes: %d"), BytesSent);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Response sent successfully, bytes: %d"), BytesSent);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Missing 'type' field in command"));
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Missing 'type' field in command"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to parse JSON from: %s"), *ReceivedText);
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to parse JSON from: %s"), *ReceivedText);
}
}
else
@@ -118,7 +119,7 @@ uint32 FMCPServerRunnable::Run()
// Check for "would block" error which isn't a real error for non-blocking sockets
if (LastError == SE_EWOULDBLOCK)
{
UE_LOG(LogTemp, Verbose, TEXT("MCPServerRunnable: Socket would block, continuing..."));
UE_LOG(LogUnrealMCP, Verbose, TEXT("MCPServerRunnable: Socket would block, continuing..."));
bShouldBreak = false;
// Small sleep to prevent tight loop when no data
FPlatformProcess::Sleep(0.01f);
@@ -126,12 +127,12 @@ uint32 FMCPServerRunnable::Run()
// Check for other transient errors we might want to tolerate
else if (LastError == SE_EINTR) // Interrupted system call
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Socket read interrupted, continuing..."));
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Socket read interrupted, continuing..."));
bShouldBreak = false;
}
else
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Client disconnected or error. Last error code: %d"), LastError);
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Client disconnected or error. Last error code: %d"), LastError);
}
if (bShouldBreak)
@@ -143,7 +144,7 @@ uint32 FMCPServerRunnable::Run()
}
else
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to accept client connection"));
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to accept client connection"));
}
}
@@ -151,7 +152,7 @@ uint32 FMCPServerRunnable::Run()
FPlatformProcess::Sleep(0.1f);
}
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Server thread stopping"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Server thread stopping"));
return 0;
}
@@ -168,44 +169,44 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr<FSocket> InClientSock
{
if (!InClientSocket.IsValid())
{
UE_LOG(LogTemp, Error, TEXT("MCPServerRunnable: Invalid client socket passed to HandleClientConnection"));
UE_LOG(LogUnrealMCP, Error, TEXT("MCPServerRunnable: Invalid client socket passed to HandleClientConnection"));
return;
}
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Starting to handle client connection"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Starting to handle client connection"));
// Set socket options for better connection stability
InClientSocket->SetNonBlocking(false);
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Set socket to blocking mode"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Set socket to blocking mode"));
// Properly read full message with timeout
const int32 MaxBufferSize = 4096;
uint8 Buffer[MaxBufferSize];
FString MessageBuffer;
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Starting message receive loop"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Starting message receive loop"));
while (bRunning && InClientSocket.IsValid())
{
// Log socket state
bool bIsConnected = InClientSocket->GetConnectionState() == SCS_Connected;
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Socket state - Connected: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Socket state - Connected: %s"),
bIsConnected ? TEXT("true") : TEXT("false"));
// Log pending data status before receive
uint32 PendingDataSize = 0;
bool HasPendingData = InClientSocket->HasPendingData(PendingDataSize);
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Before Recv - HasPendingData=%s, Size=%d"),
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Before Recv - HasPendingData=%s, Size=%d"),
HasPendingData ? TEXT("true") : TEXT("false"), PendingDataSize);
// Try to receive data with timeout
int32 BytesRead = 0;
bool bReadSuccess = false;
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Attempting to receive data..."));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Attempting to receive data..."));
bReadSuccess = InClientSocket->Recv(Buffer, MaxBufferSize, BytesRead, ESocketReceiveFlags::None);
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Recv attempt complete - Success=%s, BytesRead=%d"),
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Recv attempt complete - Success=%s, BytesRead=%d"),
bReadSuccess ? TEXT("true") : TEXT("false"), BytesRead);
if (BytesRead > 0)
@@ -216,13 +217,13 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr<FSocket> InClientSock
{
HexData += FString::Printf(TEXT("%02X "), Buffer[i]);
}
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Raw data (first 50 bytes hex): %s%s"),
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Raw data (first 50 bytes hex): %s%s"),
*HexData, BytesRead > 50 ? TEXT("...") : TEXT(""));
// Convert and log received data
Buffer[BytesRead] = 0; // Null terminate
FString ReceivedData = UTF8_TO_TCHAR(Buffer);
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Received data as string: '%s'"), *ReceivedData);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Received data as string: '%s'"), *ReceivedData);
// Append to message buffer
MessageBuffer.Append(ReceivedData);
@@ -230,34 +231,34 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr<FSocket> InClientSock
// Process complete messages (messages are terminated with newline)
if (MessageBuffer.Contains(TEXT("\n")))
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Newline detected in buffer, processing messages"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Newline detected in buffer, processing messages"));
TArray<FString> Messages;
MessageBuffer.ParseIntoArray(Messages, TEXT("\n"), true);
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Found %d message(s) in buffer"), Messages.Num());
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Found %d message(s) in buffer"), Messages.Num());
// Process all complete messages
for (int32 i = 0; i < Messages.Num() - 1; ++i)
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Processing message %d: '%s'"),
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Processing message %d: '%s'"),
i + 1, *Messages[i]);
ProcessMessage(InClientSocket, Messages[i]);
}
// Keep any incomplete message in the buffer
MessageBuffer = Messages.Last();
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Remaining buffer after processing: %s"),
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Remaining buffer after processing: %s"),
*MessageBuffer);
}
else
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: No complete message yet (no newline detected)"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: No complete message yet (no newline detected)"));
}
}
else if (!bReadSuccess)
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Connection closed or error occurred - Last error: %d"),
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Connection closed or error occurred - Last error: %d"),
(int32)ISocketSubsystem::Get()->GetLastErrorCode());
break;
}
@@ -266,12 +267,12 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr<FSocket> InClientSock
FPlatformProcess::Sleep(0.01f);
}
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Exited message receive loop"));
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Exited message receive loop"));
}
void FMCPServerRunnable::ProcessMessage(TSharedPtr<FSocket> Client, const FString& Message)
{
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Processing message: %s"), *Message);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Processing message: %s"), *Message);
// Parse message as JSON
TSharedPtr<FJsonObject> JsonMessage;
@@ -279,7 +280,7 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr<FSocket> Client, const FStrin
if (!FJsonSerializer::Deserialize(Reader, JsonMessage) || !JsonMessage.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to parse message as JSON"));
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to parse message as JSON"));
return;
}
@@ -289,7 +290,7 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr<FSocket> Client, const FStrin
if (!JsonMessage->TryGetStringField(TEXT("command"), CommandType))
{
UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Message missing 'command' field"));
UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Message missing 'command' field"));
return;
}
@@ -303,7 +304,7 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr<FSocket> Client, const FStrin
}
}
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Executing command: %s"), *CommandType);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Executing command: %s"), *CommandType);
// Execute command
FString Response = Bridge->ExecuteCommand(CommandType, Params);
@@ -312,10 +313,10 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr<FSocket> Client, const FStrin
Response += TEXT("\n");
int32 BytesSent = 0;
UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response);
UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response);
if (!Client->Send((uint8*)TCHAR_TO_UTF8(*Response), Response.Len(), BytesSent))
{
UE_LOG(LogTemp, Error, TEXT("MCPServerRunnable: Failed to send response"));
UE_LOG(LogUnrealMCP, Error, TEXT("MCPServerRunnable: Failed to send response"));
}
}
+12 -11
View File
@@ -1,4 +1,5 @@
#include "UnrealMCPBridge.h"
#include "UnrealMCPLog.h"
#include "MCPServerRunnable.h"
#include "Sockets.h"
#include "SocketSubsystem.h"
@@ -83,7 +84,7 @@ UUnrealMCPBridge::~UUnrealMCPBridge()
// Initialize subsystem
void UUnrealMCPBridge::Initialize(FSubsystemCollectionBase& Collection)
{
UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Initializing"));
UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Initializing"));
bIsRunning = false;
ListenerSocket = nullptr;
@@ -99,7 +100,7 @@ void UUnrealMCPBridge::Initialize(FSubsystemCollectionBase& Collection)
// Clean up resources when subsystem is destroyed
void UUnrealMCPBridge::Deinitialize()
{
UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Shutting down"));
UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Shutting down"));
StopServer();
}
@@ -108,7 +109,7 @@ void UUnrealMCPBridge::StartServer()
{
if (bIsRunning)
{
UE_LOG(LogTemp, Warning, TEXT("UnrealMCPBridge: Server is already running"));
UE_LOG(LogUnrealMCP, Warning, TEXT("UnrealMCPBridge: Server is already running"));
return;
}
@@ -116,7 +117,7 @@ void UUnrealMCPBridge::StartServer()
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
if (!SocketSubsystem)
{
UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to get socket subsystem"));
UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to get socket subsystem"));
return;
}
@@ -124,7 +125,7 @@ void UUnrealMCPBridge::StartServer()
TSharedPtr<FSocket> NewListenerSocket = MakeShareable(SocketSubsystem->CreateSocket(NAME_Stream, TEXT("UnrealMCPListener"), false));
if (!NewListenerSocket.IsValid())
{
UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to create listener socket"));
UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to create listener socket"));
return;
}
@@ -136,20 +137,20 @@ void UUnrealMCPBridge::StartServer()
FIPv4Endpoint Endpoint(ServerAddress, Port);
if (!NewListenerSocket->Bind(*Endpoint.ToInternetAddr()))
{
UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to bind listener socket to %s:%d"), *ServerAddress.ToString(), Port);
UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to bind listener socket to %s:%d"), *ServerAddress.ToString(), Port);
return;
}
// Start listening
if (!NewListenerSocket->Listen(5))
{
UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to start listening"));
UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to start listening"));
return;
}
ListenerSocket = NewListenerSocket;
bIsRunning = true;
UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Server started on %s:%d"), *ServerAddress.ToString(), Port);
UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Server started on %s:%d"), *ServerAddress.ToString(), Port);
// Start server thread
ServerThread = FRunnableThread::Create(
@@ -160,7 +161,7 @@ void UUnrealMCPBridge::StartServer()
if (!ServerThread)
{
UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to create server thread"));
UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to create server thread"));
StopServer();
return;
}
@@ -197,13 +198,13 @@ void UUnrealMCPBridge::StopServer()
ListenerSocket.Reset();
}
UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Server stopped"));
UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Server stopped"));
}
// Execute a command received from a client
FString UUnrealMCPBridge::ExecuteCommand(const FString& CommandType, const TSharedPtr<FJsonObject>& Params)
{
UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Executing command: %s"), *CommandType);
UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Executing command: %s"), *CommandType);
// Create a promise to wait for the result
TPromise<FString> Promise;
+5 -2
View File
@@ -1,19 +1,22 @@
#include "UnrealMCPModule.h"
#include "UnrealMCPLog.h"
#include "UnrealMCPBridge.h"
#include "Modules/ModuleManager.h"
#include "EditorSubsystem.h"
#include "Editor.h"
DEFINE_LOG_CATEGORY(LogUnrealMCP);
#define LOCTEXT_NAMESPACE "FUnrealMCPModule"
void FUnrealMCPModule::StartupModule()
{
UE_LOG(LogTemp, Display, TEXT("Unreal MCP Module has started"));
UE_LOG(LogUnrealMCP, Display, TEXT("Unreal MCP Module has started"));
}
void FUnrealMCPModule::ShutdownModule()
{
UE_LOG(LogTemp, Display, TEXT("Unreal MCP Module has shut down"));
UE_LOG(LogUnrealMCP, Display, TEXT("Unreal MCP Module has shut down"));
}
#undef LOCTEXT_NAMESPACE
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "Logging/LogMacros.h"
DECLARE_LOG_CATEGORY_EXTERN(LogUnrealMCP, Log, All);
@@ -0,0 +1,487 @@
#include "ClaudeCodeInitializer.h"
#include "UnrealMCPSetupLog.h"
#include "ClaudeCodeResultDialog.h"
#include "Dom/JsonObject.h"
#include "HAL/FileManager.h"
#include "HAL/PlatformFileManager.h"
#include "HAL/PlatformProcess.h"
#include "Misc/App.h"
#include "Misc/FileHelper.h"
#include "Misc/MessageDialog.h"
#include "Misc/Paths.h"
#include "Misc/ScopedSlowTask.h"
#include "Interfaces/IPluginManager.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
#define LOCTEXT_NAMESPACE "FClaudeCodeInitializer"
void FClaudeCodeInitializer::Run()
{
if (!IsCppProject())
{
UE_LOG(LogUnrealMCPSetup, Warning, TEXT("Init aborted: not a C++ project (no .Build.cs found)"));
FMessageDialog::Open(EAppMsgType::Ok,
LOCTEXT("NotCppError", "Claude Code ne peut être initialisé que dans un projet C++."));
return;
}
const EAppReturnType::Type Answer = FMessageDialog::Open(EAppMsgType::YesNo,
LOCTEXT("ConfirmInit", "Êtes-vous sûr de vouloir initialiser Claude Code pour ce projet ?"));
if (Answer != EAppReturnType::Yes)
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("Init cancelled by user"));
return;
}
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(TEXT("UnrealMCP"));
if (!Plugin.IsValid())
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("Plugin 'UnrealMCP' not found via IPluginManager"));
FMessageDialog::Open(EAppMsgType::Ok,
LOCTEXT("PluginNotFound", "Plugin UnrealMCP introuvable."));
return;
}
const FString PluginDir = Plugin->GetBaseDir();
const FString ProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir());
UE_LOG(LogUnrealMCPSetup, Display, TEXT("Starting Claude Code init — PluginDir: %s | ProjectDir: %s"), *PluginDir, *ProjectDir);
FInitResult Result;
{
FScopedSlowTask SlowTask(5.0f, LOCTEXT("Working", "Initialisation de Claude Code..."));
SlowTask.MakeDialog(false, false);
SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepCopy", "Vérification du serveur Python..."));
Result.Steps.Add(CopyPythonServer(PluginDir, ProjectDir));
SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepClaudeAssets", "Copie des commandes et skills Claude Code..."));
Result.Steps.Add(CopyClaudeAssets(PluginDir, ProjectDir));
SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepMcp", "Vérification de .mcp.json..."));
Result.Steps.Add(WriteMcpJson(ProjectDir));
SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepSettings", "Vérification des permissions MCP..."));
Result.Steps.Add(WriteClaudeLocalSettings(ProjectDir));
SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepUv", "Initialisation de l'environnement Python (uv sync)..."));
Result.Steps.Add(RunUvSync(ProjectDir));
}
Result.bOverallSuccess = true;
for (const FInitStep& Step : Result.Steps)
{
if (!Step.bSuccess)
{
Result.bOverallSuccess = false;
break;
}
}
if (Result.bOverallSuccess)
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("Claude Code init completed successfully"));
}
else
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("Claude Code init finished with errors — check Output Log for details"));
}
FClaudeCodeResultDialog::Show(Result);
}
bool FClaudeCodeInitializer::IsCppProject()
{
const FString BuildCs = FPaths::ProjectDir()
/ TEXT("Source")
/ FApp::GetProjectName()
/ (FString(FApp::GetProjectName()) + TEXT(".Build.cs"));
return FPaths::FileExists(BuildCs);
}
// ---------------------------------------------------------------------------
// Validation helpers
// ---------------------------------------------------------------------------
bool FClaudeCodeInitializer::IsMcpJsonValid(const FString& ProjectDir)
{
const FString FilePath = ProjectDir / TEXT(".mcp.json");
FString RawContent;
if (!FFileHelper::LoadFileToString(RawContent, *FilePath))
{
return false;
}
TSharedPtr<FJsonObject> Root;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(RawContent);
if (!FJsonSerializer::Deserialize(Reader, Root) || !Root.IsValid())
{
return false;
}
const TSharedPtr<FJsonObject>* McpServers;
if (!Root->TryGetObjectField(TEXT("mcpServers"), McpServers))
{
return false;
}
const TSharedPtr<FJsonObject>* UnrealMCP;
if (!(*McpServers)->TryGetObjectField(TEXT("unrealMCP"), UnrealMCP))
{
return false;
}
FString Command;
if (!(*UnrealMCP)->TryGetStringField(TEXT("command"), Command) || Command != TEXT("uv"))
{
return false;
}
const TArray<TSharedPtr<FJsonValue>>* Args;
if (!(*UnrealMCP)->TryGetArrayField(TEXT("args"), Args))
{
return false;
}
for (const TSharedPtr<FJsonValue>& Arg : *Args)
{
if (Arg->AsString() == TEXT("unreal_mcp_server.py"))
{
return true;
}
}
return false;
}
bool FClaudeCodeInitializer::IsMcpServerFolderValid(const FString& ProjectDir)
{
const FString EntryPoint = ProjectDir / TEXT(".claude/mcp-server/unreal_mcp_server.py");
return FPaths::FileExists(EntryPoint);
}
bool FClaudeCodeInitializer::IsClaudeLocalSettingsValid(const FString& ProjectDir)
{
const FString FilePath = ProjectDir / TEXT(".claude/settings.local.json");
FString RawContent;
if (!FFileHelper::LoadFileToString(RawContent, *FilePath))
{
return false;
}
TSharedPtr<FJsonObject> Root;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(RawContent);
if (!FJsonSerializer::Deserialize(Reader, Root) || !Root.IsValid())
{
return false;
}
const TSharedPtr<FJsonObject>* Permissions;
if (!Root->TryGetObjectField(TEXT("permissions"), Permissions))
{
return false;
}
const TArray<TSharedPtr<FJsonValue>>* Allow;
if (!(*Permissions)->TryGetArrayField(TEXT("allow"), Allow))
{
return false;
}
for (const TSharedPtr<FJsonValue>& Entry : *Allow)
{
if (Entry->AsString().StartsWith(TEXT("mcp__unrealMCP__")))
{
return true;
}
}
return false;
}
// ---------------------------------------------------------------------------
// Init steps — validate first, act only if needed
// ---------------------------------------------------------------------------
bool FClaudeCodeInitializer::CopyDirectoryFiltered(const FString& Src, const FString& Dst)
{
static const TArray<FString> SkippedDirs = { TEXT(".venv"), TEXT("__pycache__"), TEXT("unreal_mcp.egg-info") };
static const TArray<FString> SkippedExts = { TEXT(".log"), TEXT(".bck") };
IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile();
PF.CreateDirectoryTree(*Dst);
bool bAllOk = true;
PF.IterateDirectory(*Src, [&](const TCHAR* Path, bool bIsDirectory) -> bool
{
const FString Name = FPaths::GetCleanFilename(Path);
const FString DstPath = Dst / Name;
if (bIsDirectory)
{
if (!SkippedDirs.Contains(Name))
{
bAllOk &= CopyDirectoryFiltered(Path, DstPath);
}
}
else
{
const FString Ext = FPaths::GetExtension(Name, /*bIncludeDot*/ true).ToLower();
if (!SkippedExts.Contains(Ext))
{
if (!PF.CopyFile(*DstPath, Path))
{
UE_LOG(LogUnrealMCPSetup, Warning, TEXT("[Copy] Failed to copy file: %s"), Path);
bAllOk = false;
}
}
}
return true;
});
return bAllOk;
}
FInitStep FClaudeCodeInitializer::CopyPythonServer(const FString& PluginDir, const FString& ProjectDir)
{
FInitStep Step;
Step.Name = LOCTEXT("StepNameCopy", "Serveur Python (.claude/mcp-server)");
if (IsMcpServerFolderValid(ProjectDir))
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[PythonServer] Already valid — skipping copy"));
Step.bSuccess = true;
Step.Details = TEXT("Déjà présent et valide — aucune modification.");
return Step;
}
const FString Src = PluginDir / TEXT("PythonServer");
const FString Dst = ProjectDir / TEXT(".claude/mcp-server");
if (!FPaths::DirectoryExists(Src))
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[PythonServer] Source folder not found: %s"), *Src);
Step.bSuccess = false;
Step.Details = FString::Printf(TEXT("Dossier source introuvable : %s"), *Src);
return Step;
}
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[PythonServer] Copying %s → %s (skipping .venv, __pycache__, egg-info)"), *Src, *Dst);
if (CopyDirectoryFiltered(Src, Dst))
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[PythonServer] Copy successful"));
Step.bSuccess = true;
Step.Details = FString::Printf(TEXT("Copié : %s → %s"), *Src, *Dst);
}
else
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[PythonServer] Copy failed to %s"), *Dst);
Step.bSuccess = false;
Step.Details = FString::Printf(TEXT("Échec de la copie vers %s"), *Dst);
}
return Step;
}
FInitStep FClaudeCodeInitializer::CopyClaudeAssets(const FString& PluginDir, const FString& ProjectDir)
{
FInitStep Step;
Step.Name = LOCTEXT("StepNameClaudeAssets", "Commandes & Skills (.claude/commands, .claude/skills)");
const FString ClaudeSrc = PluginDir / TEXT(".claude");
const FString ClaudeDst = ProjectDir / TEXT(".claude");
// Check if both directories are already present
const bool bCommandsOk = FPaths::DirectoryExists(ClaudeDst / TEXT("commands"));
const bool bSkillsOk = FPaths::DirectoryExists(ClaudeDst / TEXT("skills"));
if (bCommandsOk && bSkillsOk)
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[ClaudeAssets] commands/ and skills/ already present — skipping"));
Step.bSuccess = true;
Step.Details = TEXT("Déjà présents — aucune modification.");
return Step;
}
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[ClaudeAssets] Copying commands/ and skills/ from %s → %s"), *ClaudeSrc, *ClaudeDst);
bool bAllOk = true;
for (const FString& SubDir : { FString(TEXT("commands")), FString(TEXT("skills")) })
{
const FString Src = ClaudeSrc / SubDir;
const FString Dst = ClaudeDst / SubDir;
if (FPaths::DirectoryExists(Src))
{
if (!CopyDirectoryFiltered(Src, Dst))
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[ClaudeAssets] Failed to copy %s"), *Src);
bAllOk = false;
}
}
else
{
UE_LOG(LogUnrealMCPSetup, Warning, TEXT("[ClaudeAssets] Source not found, skipping: %s"), *Src);
}
}
Step.bSuccess = bAllOk;
Step.Details = bAllOk
? FString::Printf(TEXT("Copié : commands/ et skills/ → %s"), *ClaudeDst)
: FString::Printf(TEXT("Échec partiel — voir Output Log pour les détails"));
return Step;
}
FInitStep FClaudeCodeInitializer::WriteMcpJson(const FString& ProjectDir)
{
FInitStep Step;
Step.Name = LOCTEXT("StepNameMcp", ".mcp.json");
if (IsMcpJsonValid(ProjectDir))
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Already valid — skipping write"));
Step.bSuccess = true;
Step.Details = TEXT("Déjà configuré et valide — aucune modification.");
return Step;
}
const FString FilePath = ProjectDir / TEXT(".mcp.json");
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Writing %s"), *FilePath);
const FString Content =
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(" }\n")
TEXT(" }\n")
TEXT("}\n");
if (FFileHelper::SaveStringToFile(Content, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Written successfully"));
Step.bSuccess = true;
Step.Details = FString::Printf(TEXT("Créé : %s"), *FilePath);
}
else
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[.mcp.json] Failed to write %s"), *FilePath);
Step.bSuccess = false;
Step.Details = FString::Printf(TEXT("Impossible d'écrire %s"), *FilePath);
}
return Step;
}
FInitStep FClaudeCodeInitializer::WriteClaudeLocalSettings(const FString& ProjectDir)
{
FInitStep Step;
Step.Name = LOCTEXT("StepNameSettings", "Permissions MCP (.claude/settings.local.json)");
if (IsClaudeLocalSettingsValid(ProjectDir))
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[settings.local.json] Already valid — skipping write"));
Step.bSuccess = true;
Step.Details = TEXT("Déjà configuré et valide — aucune modification.");
return Step;
}
const FString ClaudeDir = ProjectDir / TEXT(".claude");
const FString FilePath = ClaudeDir / TEXT("settings.local.json");
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[settings.local.json] Writing %s"), *FilePath);
IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile();
if (!PF.DirectoryExists(*ClaudeDir))
{
PF.CreateDirectoryTree(*ClaudeDir);
}
const FString Content =
TEXT("{\n")
TEXT(" \"permissions\": {\n")
TEXT(" \"allow\": [\n")
TEXT(" \"mcp__unrealMCP__get_actors_in_level\",\n")
TEXT(" \"mcp__unrealMCP__find_actors_by_name\",\n")
TEXT(" \"mcp__unrealMCP__spawn_actor\",\n")
TEXT(" \"mcp__unrealMCP__delete_actor\",\n")
TEXT(" \"mcp__unrealMCP__set_actor_transform\",\n")
TEXT(" \"mcp__unrealMCP__get_actor_properties\",\n")
TEXT(" \"mcp__unrealMCP__set_actor_property\",\n")
TEXT(" \"mcp__unrealMCP__spawn_blueprint_actor\",\n")
TEXT(" \"mcp__unrealMCP__create_blueprint\",\n")
TEXT(" \"mcp__unrealMCP__add_component_to_blueprint\",\n")
TEXT(" \"mcp__unrealMCP__set_static_mesh_properties\",\n")
TEXT(" \"mcp__unrealMCP__set_component_property\",\n")
TEXT(" \"mcp__unrealMCP__set_physics_properties\",\n")
TEXT(" \"mcp__unrealMCP__set_blueprint_property\",\n")
TEXT(" \"mcp__unrealMCP__compile_blueprint\",\n")
TEXT(" \"mcp__unrealMCP__find_blueprint_nodes\",\n")
TEXT(" \"mcp__unrealMCP__add_blueprint_event_node\",\n")
TEXT(" \"mcp__unrealMCP__add_blueprint_input_action_node\",\n")
TEXT(" \"mcp__unrealMCP__add_blueprint_function_node\",\n")
TEXT(" \"mcp__unrealMCP__add_blueprint_variable\",\n")
TEXT(" \"mcp__unrealMCP__add_blueprint_self_reference\",\n")
TEXT(" \"mcp__unrealMCP__add_blueprint_get_self_component_reference\",\n")
TEXT(" \"mcp__unrealMCP__connect_blueprint_nodes\",\n")
TEXT(" \"mcp__unrealMCP__create_input_mapping\",\n")
TEXT(" \"mcp__unrealMCP__create_umg_widget_blueprint\",\n")
TEXT(" \"mcp__unrealMCP__add_text_block_to_widget\",\n")
TEXT(" \"mcp__unrealMCP__add_button_to_widget\",\n")
TEXT(" \"mcp__unrealMCP__bind_widget_event\",\n")
TEXT(" \"mcp__unrealMCP__set_text_block_binding\",\n")
TEXT(" \"mcp__unrealMCP__add_widget_to_viewport\"\n")
TEXT(" ]\n")
TEXT(" }\n")
TEXT("}\n");
if (FFileHelper::SaveStringToFile(Content, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[settings.local.json] Written successfully"));
Step.bSuccess = true;
Step.Details = FString::Printf(TEXT("Créé : %s"), *FilePath);
}
else
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[settings.local.json] Failed to write %s"), *FilePath);
Step.bSuccess = false;
Step.Details = FString::Printf(TEXT("Impossible d'écrire %s"), *FilePath);
}
return Step;
}
FInitStep FClaudeCodeInitializer::RunUvSync(const FString& ProjectDir)
{
FInitStep Step;
Step.Name = LOCTEXT("StepNameUv", "Environnement Python (uv sync)");
const FString McpServerDir = ProjectDir / TEXT(".claude/mcp-server");
// --no-install-project: install dependencies only, skip building the local package.
// Building it spawns a grandchild Python process that inherits invalid pipe handles
// from FPlatformProcess::ExecProcess on Windows (os error 6 / ERROR_INVALID_HANDLE).
// unreal_mcp_server.py is a plain script — it doesn't need to be installed as a package.
const FString Args = FString::Printf(TEXT("sync --directory \"%s\" --no-install-project"), *McpServerDir);
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[uv sync] Running: uv %s"), *Args);
int32 ReturnCode = 0;
FString StdOut, StdErr;
const bool bLaunched = FPlatformProcess::ExecProcess(
TEXT("uv"), *Args, &ReturnCode, &StdOut, &StdErr);
if (!bLaunched)
{
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[uv sync] 'uv' not found in PATH"));
Step.bSuccess = false;
Step.Details = TEXT("uv introuvable dans le PATH. Installez uv (https://docs.astral.sh/uv/) puis relancez.");
}
else if (ReturnCode == 0)
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("[uv sync] Completed successfully"));
Step.bSuccess = true;
Step.Details = TEXT("Environnement Python prêt.");
}
else
{
const FString ErrorMsg = StdErr.IsEmpty() ? StdOut : StdErr;
UE_LOG(LogUnrealMCPSetup, Error, TEXT("[uv sync] Failed (code %d): %s"), ReturnCode, *ErrorMsg);
Step.bSuccess = false;
Step.Details = ErrorMsg;
}
return Step;
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,48 @@
#include "ClaudeCodeMenuExtender.h"
#include "UnrealMCPSetupLog.h"
#include "ClaudeCodeInitializer.h"
#include "ToolMenus.h"
#include "ToolMenu.h"
#include "ToolMenuSection.h"
#define LOCTEXT_NAMESPACE "FClaudeCodeMenuExtender"
void FClaudeCodeMenuExtender::Register()
{
// Pattern from UE5 source (LevelEditorMenu.cpp ExtendMenuBar):
// - ExtendMenu + FindOrAddSection(NAME_None)
// - InsertPosition on the ENTRY, not the section
// - Anchor name is "Help" (short form, not the full qualified name)
UToolMenu* MenuBar = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu");
FToolMenuSection& Section = MenuBar->FindOrAddSection(NAME_None);
FToolMenuEntry& SubMenu = Section.AddSubMenu(
"ClaudeCodeMenu",
LOCTEXT("MenuLabel", "Claude Code"),
LOCTEXT("MenuTooltip", "Claude Code tools for this project"),
FNewToolMenuDelegate::CreateStatic(&FClaudeCodeMenuExtender::BuildMenu)
);
// "Actor" in the menu bar is the entry named "Actions" (dynamic label, see LevelEditorMenu.cpp).
// Using After("Actions") places Claude Code between Actor and Help.
SubMenu.InsertPosition = FToolMenuInsert("Actions", EToolMenuInsertType::After);
}
void FClaudeCodeMenuExtender::BuildMenu(UToolMenu* Menu)
{
FToolMenuSection& Section = Menu->FindOrAddSection("ClaudeCodeActions");
Section.AddMenuEntry(
"InitClaudeCode",
LOCTEXT("InitLabel", "Init Claude Code"),
LOCTEXT("InitTooltip", "Initialisation de Claude Code pour ce projet"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateStatic(&FClaudeCodeMenuExtender::OnInitClaudeCodeClicked))
);
}
void FClaudeCodeMenuExtender::OnInitClaudeCodeClicked()
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("User clicked 'Init Claude Code'"));
FClaudeCodeInitializer::Run();
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,138 @@
#include "ClaudeCodeResultDialog.h"
#include "Framework/Application/SlateApplication.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SWindow.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Text/STextBlock.h"
#define LOCTEXT_NAMESPACE "FClaudeCodeResultDialog"
void FClaudeCodeResultDialog::Show(const FInitResult& Result)
{
TSharedRef<SWindow> Window = SNew(SWindow)
.Title(LOCTEXT("DialogTitle", "Claude Code — Résultat de l'initialisation"))
.ClientSize(FVector2D(540, 340))
.SupportsMaximize(false)
.SupportsMinimize(false)
.IsTopmostWindow(true);
Window->SetContent(
SNew(SClaudeCodeResultDialog)
.InitResult(Result)
.ParentWindow(Window)
);
FSlateApplication::Get().AddModalWindow(Window, FSlateApplication::Get().GetActiveTopLevelWindow());
}
void SClaudeCodeResultDialog::Construct(const FArguments& InArgs)
{
ParentWindow = InArgs._ParentWindow;
const FInitResult& Result = InArgs._InitResult;
TSharedRef<SVerticalBox> StepsList = SNew(SVerticalBox);
for (const FInitStep& Step : Result.Steps)
{
const FString Icon = Step.bSuccess ? TEXT("✓") : TEXT("✗");
const FSlateColor IconColor = Step.bSuccess
? FSlateColor(FLinearColor(0.2f, 0.85f, 0.2f))
: FSlateColor(FLinearColor(0.9f, 0.2f, 0.2f));
StepsList->AddSlot()
.AutoHeight()
.Padding(4.f, 4.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Top)
.Padding(0.f, 1.f, 10.f, 0.f)
[
SNew(STextBlock)
.Text(FText::FromString(Icon))
.ColorAndOpacity(IconColor)
]
+ SHorizontalBox::Slot()
.FillWidth(1.f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(Step.Name)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.f, 2.f, 0.f, 0.f)
[
SNew(STextBlock)
.Text(FText::FromString(Step.Details))
.ColorAndOpacity(FSlateColor(FLinearColor(0.55f, 0.55f, 0.55f)))
.AutoWrapText(true)
]
]
];
}
const FText StatusText = Result.bOverallSuccess
? LOCTEXT("StatusOk", "Initialisation réussie. Redémarrez Claude Code pour activer le serveur MCP.")
: LOCTEXT("StatusFail", "L'initialisation a rencontré des erreurs. Consultez les détails ci-dessus.");
const FSlateColor StatusColor = Result.bOverallSuccess
? FSlateColor(FLinearColor(0.2f, 0.85f, 0.2f))
: FSlateColor(FLinearColor(0.9f, 0.2f, 0.2f));
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.f)
.Padding(14.f, 12.f)
[
SNew(SScrollBox)
+ SScrollBox::Slot()
[
StepsList
]
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SSeparator)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(14.f, 8.f)
[
SNew(STextBlock)
.Text(StatusText)
.ColorAndOpacity(StatusColor)
.AutoWrapText(true)
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(14.f, 4.f, 14.f, 14.f)
[
SNew(SButton)
.Text(LOCTEXT("CloseBtn", "Fermer"))
.OnClicked(this, &SClaudeCodeResultDialog::OnCloseClicked)
]
];
}
FReply SClaudeCodeResultDialog::OnCloseClicked()
{
if (ParentWindow.IsValid())
{
ParentWindow->RequestDestroyWindow();
}
return FReply::Handled();
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,40 @@
#include "UnrealMCPSetupModule.h"
#include "UnrealMCPSetupLog.h"
#include "ClaudeCodeMenuExtender.h"
#include "ToolMenus.h"
#include "Editor.h"
DEFINE_LOG_CATEGORY(LogUnrealMCPSetup);
#define LOCTEXT_NAMESPACE "FUnrealMCPSetupModule"
void FUnrealMCPSetupModule::StartupModule()
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("UnrealMCPSetup module started"));
if (GEditor)
{
UToolMenus::RegisterStartupCallback(
FSimpleMulticastDelegate::FDelegate::CreateRaw(
this, &FUnrealMCPSetupModule::RegisterMenus));
}
else
{
UE_LOG(LogUnrealMCPSetup, Warning, TEXT("GEditor not available — Claude Code menu will not be registered"));
}
}
void FUnrealMCPSetupModule::ShutdownModule()
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("UnrealMCPSetup module shut down"));
UToolMenus::UnRegisterStartupCallback(this);
}
void FUnrealMCPSetupModule::RegisterMenus()
{
UE_LOG(LogUnrealMCPSetup, Display, TEXT("Registering Claude Code menu"));
FClaudeCodeMenuExtender::Register();
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FUnrealMCPSetupModule, UnrealMCPSetup)
@@ -0,0 +1,27 @@
#pragma once
#include "CoreMinimal.h"
#include "ClaudeCodeTypes.h"
class FClaudeCodeInitializer
{
public:
static void Run();
private:
static bool IsCppProject();
static FInitStep CopyPythonServer(const FString& PluginDir, const FString& ProjectDir);
static FInitStep CopyClaudeAssets(const FString& PluginDir, const FString& ProjectDir);
static FInitStep WriteMcpJson(const FString& ProjectDir);
static FInitStep WriteClaudeLocalSettings(const FString& ProjectDir);
static FInitStep RunUvSync(const FString& ProjectDir);
// Validation helpers (used when .claude/ already exists)
static bool IsMcpJsonValid(const FString& ProjectDir);
static bool IsMcpServerFolderValid(const FString& ProjectDir);
static bool IsClaudeLocalSettingsValid(const FString& ProjectDir);
// Recursive copy that skips .venv, __pycache__, *.egg-info (locked / generated files)
static bool CopyDirectoryFiltered(const FString& Src, const FString& Dst);
};
@@ -0,0 +1,15 @@
#pragma once
#include "CoreMinimal.h"
class UToolMenu;
class FClaudeCodeMenuExtender
{
public:
static void Register();
private:
static void BuildMenu(UToolMenu* Menu);
static void OnInitClaudeCodeClicked();
};
@@ -0,0 +1,27 @@
#pragma once
#include "CoreMinimal.h"
#include "ClaudeCodeTypes.h"
#include "Widgets/SCompoundWidget.h"
class FClaudeCodeResultDialog
{
public:
static void Show(const FInitResult& Result);
};
class SClaudeCodeResultDialog : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SClaudeCodeResultDialog) {}
SLATE_ARGUMENT(FInitResult, InitResult)
SLATE_ARGUMENT(TSharedPtr<SWindow>, ParentWindow)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs);
private:
FReply OnCloseClicked();
TSharedPtr<SWindow> ParentWindow;
};
@@ -0,0 +1,16 @@
#pragma once
#include "CoreMinimal.h"
struct FInitStep
{
FText Name;
bool bSuccess = false;
FString Details;
};
struct FInitResult
{
bool bOverallSuccess = false;
TArray<FInitStep> Steps;
};
@@ -0,0 +1,5 @@
#pragma once
#include "Logging/LogMacros.h"
DECLARE_LOG_CATEGORY_EXTERN(LogUnrealMCPSetup, Log, All);
@@ -0,0 +1,14 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FUnrealMCPSetupModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
void RegisterMenus();
};
@@ -0,0 +1,23 @@
using UnrealBuildTool;
public class UnrealMCPSetup : ModuleRules
{
public UnrealMCPSetup(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
IWYUSupport = IWYUSupport.Full;
PrivateDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"UnrealEd",
"ToolMenus",
"Projects",
"Json",
});
}
}
+10
View File
@@ -24,6 +24,16 @@
"Mac",
"Linux"
]
},
{
"Name": "UnrealMCPSetup",
"Type": "Editor",
"LoadingPhase": "PostEngineInit",
"WhitelistPlatforms": [
"Win64",
"Mac",
"Linux"
]
}
],
"Plugins": [