Skip to content

feat:phase 42 ai npc plugins#469

Open
Jbrandan wants to merge 4 commits into
ChiR24:devfrom
Jbrandan:feat/phase-42-ai-npc-plugins
Open

feat:phase 42 ai npc plugins#469
Jbrandan wants to merge 4 commits into
ChiR24:devfrom
Jbrandan:feat/phase-42-ai-npc-plugins

Conversation

@Jbrandan

Copy link
Copy Markdown

Summary

Implements Phase 42: AI & NPC Plugins — extends the existing manage_ai tool with 22 new actions covering NPC dialogue systems, adaptive behavior modes (Patrol/Alert/Combat/Idle), NPC Director with dynamic spawning and group tactics, and a memory/personality system for believable NPCs.

All new C++ handlers are isolated in a new Domains/AI/NPC/ subdirectory, following the project's separation-of-concerns pattern. No new MCP tool is introduced; all 22 actions are consolidated under the canonical manage_ai surface.

Changes

  • [NEW] src/tools/definitions/gameplay/ai/manage-ai-npc-properties.ts — TS schema properties for all 22 NPC actions (dialoguePath, waypointList, personalityTraits, groupTactic, etc.)
  • [MOD] src/tools/definitions/gameplay/ai/manage-ai-behavior-properties.ts — Added 22 new NPC action strings to the action enum
  • [MOD] src/tools/definitions/gameplay/ai/manage-ai-input-schema.ts — Spread manageAiNpcProperties into the input schema
  • [NEW] plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCDialogue.cpp — 6 dialogue handlers (create_npc_dialogue_tree, add_dialogue_node, link_dialogue_nodes, set_dialogue_speaker, set_dialogue_condition, trigger_dialogue)
  • [NEW] plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp — 6 behavior mode handlers (setup_patrol_mode, setup_alert_mode, setup_combat_mode, setup_idle_mode, configure_mode_transitions, add_patrol_waypoint)
  • [NEW] plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCDirector.cpp — 6 NPC Director / spawn handlers (create_npc_spawner, configure_spawn_limits, set_spawn_conditions, create_npc_group, configure_group_tactics, get_npc_state)
  • [NEW] plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCMemory.cpp — 6 memory & personality handlers (create_npc_memory, add_memory_record, query_npc_memory, set_npc_personality, configure_reputation_system, get_npc_info)
  • [MOD] plugins/.../Domains/AI/McpAutomationBridge_AIHandlerContext.h — Declared 22 new handler functions via MCP_AI_HANDLER_DECL
  • [MOD] plugins/.../Domains/AI/McpAutomationBridge_AIHandlers.cpp — Added 22 dispatch cases to HandleManageAIAction
  • [MOD] plugins/.../MCP/Routing/McpConsolidatedActionRoutingAI.h — Registered all 22 actions in ManageAICore()
  • [MOD] plugins/.../MCP/Tools/Gameplay/McpTool_ManageAI.cpp — Added NPC schema fields to native tool definition (60 → 82 actions)

Related Issues

Related to #Phase-42

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 📚 Documentation update
  • 🔧 Configuration/build change
  • ♻️ Refactoring (no functional changes)
  • 🧪 Test addition/update

Testing

  • Tested with Unreal Engine (version: 5.6)
  • Tested MCP client integration (client: Smoke Test Mock)
  • Added/updated tests

Pre-Merge Checklist

  • Code follows project style guidelines
  • Self-reviewed the code
  • Updated relevant documentation (if needed)
  • Added/updated tests (if applicable)
  • CI passes

Jbrandan added 4 commits June 12, 2026 00:02
…s, spawning, memory

Adds 22 new actions to manage_ai tool (Phase 42):

42.1 NPC Dialogue System (6 actions)
- create_npc_dialogue_tree, add_dialogue_node, link_dialogue_nodes
- set_dialogue_speaker, set_dialogue_condition, trigger_dialogue

42.2 NPC Behavior Modes (6 actions)
- setup_patrol_mode, setup_alert_mode, setup_combat_mode, setup_idle_mode
- configure_mode_transitions, add_patrol_waypoint

42.3 NPC Director & Spawning (6 actions)
- create_npc_spawner, configure_spawn_limits, set_spawn_conditions
- create_npc_group, configure_group_tactics, get_npc_state

42.4 NPC Memory & Personality (6 actions)
- create_npc_memory, add_memory_record, query_npc_memory
- set_npc_personality, configure_reputation_system, get_npc_info

New C++ handlers isolated in Domains/AI/NPC/ subdirectory.
Native MCP schema updated (60 -> 82 actions).
build:core and test:smoke pass.
@github-actions

Copy link
Copy Markdown
Contributor

📏 Large PR Detected

This pull request is quite large (1000+ lines changed), which can make reviewing challenging.

Suggestions:

  • Consider breaking this into smaller, focused PRs
  • Separate refactoring from new features
  • Split bug fixes from feature additions

This helps reviewers provide better feedback and speeds up the merge process. Thank you! 🙏

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • NPC dialogue system creation and management
    • NPC behavior modes (patrol, alert, combat, idle) with configurable transitions
    • NPC spawner and group management with tactics configuration
    • NPC memory and personality system
    • Data management tools for configurations, save games, and gameplay tags
    • Asset browser utilities for navigation, collections, and asset organization
    • Actor selection and grouping functionality
    • Editor grid and snap settings configuration
    • Project settings management tool
  • Tests

    • Added integration tests for data management and dynamic handlers

Walkthrough

This PR extends the MCP automation bridge with Phase 42 NPC dialogue/behavior/memory systems (22 new actions), Phase 31 data/config persistence handlers, a new manage_project_settings tool, Phase 34 asset and control utilities, and applies prepatory patches to widget authoring for synchronous execution, sanitization, and validation.

Changes

Widget Authoring and UMG Synchronization Fixes

Layer / File(s) Summary
C++ Widget and UMG Include Restructuring and Async-to-Sync Conversion
patch_cpp_feedback.py
Python build-time patch removes FileManage/ and Widget/ include prefixes across three UMG source files and converts ApplyJsonStringToUmgAsset from always-async to synchronous execution on game thread with TaskGraph fallback for off-thread callers.
C++ Handler Path Sanitization and TypeScript JSON/Path Validation
patch_handlers.py, patch_ts_handlers.py
C++ widget authoring handler patch removes dead reparent_widget code, adds GEditor null guards, and wraps widget path with SanitizeProjectRelativePath. TypeScript patch injects requireValidJson helper and wraps widget paths with sanitizePath across action cases.

Phase 42: NPC Dialogue, Behavior, Director, and Memory Systems

Layer / File(s) Summary
NPC Handler Declarations and Context Setup
plugins/.../Domains/AI/McpAutomationBridge_AIHandlerContext.h
New macro-declared handler function prototypes for dialogue, behavior modes, spawning, grouping, memory, and personality subsystems.
NPC Dialogue Tree and Node Management Implementation
plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCDialogue.cpp
Six editor-only handlers create dialogue trees, add/link nodes, set speakers/conditions, and trigger dialogue with validation and EDITOR_ONLY fallback.
NPC Behavior Mode Setup and Mode Transition Configuration
plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp
Six editor-only handlers configure patrol/alert/combat/idle modes with mode-specific parameters, transition conditions, and waypoint management.
NPC Spawning, Grouping, and State Management Implementation
plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCDirector.cpp
Six editor-only handlers create spawners (with limits/conditions), groups (with leader/tactics), and retrieve NPC state with parameter validation.
NPC Memory, Personality Traits, and Reputation System Implementation
plugins/.../Domains/AI/NPC/McpAutomationBridge_NPCMemory.cpp
Six editor-only handlers manage memory lifecycle, personality traits with numeric defaults, faction reputation, and aggregate NPC info retrieval.
NPC Action Routing and AI Handler Dispatch Extension
plugins/.../Domains/AI/McpAutomationBridge_AIHandlers.cpp
Handler dispatch recognizes 22 new Phase 42 NPC subActions (dialogue, behavior, director, memory) and routes to corresponding implementations.
TypeScript NPC Properties Schema Definition
src/tools/definitions/gameplay/ai/manage-ai-npc-properties.ts
New exported schema object defines dialogue, behavior mode, spawner/group, and memory/personality input field structure.
Manage AI Tool Schema and Behavior Properties Expansion
plugins/.../MCP/Tools/Gameplay/McpTool_ManageAI.cpp, src/tools/definitions/gameplay/ai/manage-ai-behavior-properties.ts
Tool action count increased 60 → 82; schema adds NPC dialogue, mode, spawner, group, memory, reputation fields.
Manage AI Input Schema Composition and Consolidated Action Routing
src/tools/definitions/gameplay/ai/manage-ai-input-schema.ts, plugins/.../MCP/Routing/McpConsolidatedActionRoutingAI.h
Input schema spreads NPC properties into composition; consolidated routing registers all 22 Phase 42 NPC actions in ManageAICore() array.

Phase 31: Data, Config, and Persistence Management

Layer / File(s) Summary
Data Handler Header and Initialization Wiring
plugins/.../Domains/Data/McpAutomationBridge_DataHandlers.h, plugins/.../Core/Subsystem/McpAutomationBridgeSubsystemHandlerRegistration.cpp
New FMcpAutomationBridge_DataHandlers class with RegisterHandlers static method is declared and wired into subsystem initialization.
Data Handler Implementation with Config, Save, and Gameplay Tag Support
plugins/.../Domains/Data/McpAutomationBridge_DataHandlers.cpp
RegisterHandlers() registers manage_data websocket handler dispatching config read/write/flush, save-slot existence/deletion, gameplay-tag creation, and data-asset placeholders.
TypeScript Data Handler and Tool Definition
src/tools/handlers/data/data-handlers.ts, src/tools/definitions/gameplay/manage-data-tool.ts
TypeScript handleDataTools constructs payloads with subAction and delegates to automation bridge; tool definition exports input action enum and output response schema.
Data Handler Orchestration and Routing Setup
src/tools/orchestration/consolidated-routing.ts, src/tools/orchestration/consolidated-handler-registration.ts, src/tools/definitions/shared/all-tool-definitions.ts
New dataActionSet exported; handler registry and orchestration wire handleDataTools to manage_data tool; tool included in global registry.
Data Handler Integration Test Suite
tests/mcp-tools/gameplay/manage-data.test.mjs
Integration tests exercise config operations, save-slot manipulation, gameplay-tag creation, and data-asset actions with timestamped fixtures.

Project Settings Configuration Tool (New)

Layer / File(s) Summary
Project Settings Handler Implementation and Registration
plugins/.../Domains/Project/McpAutomationBridge_ProjectSettingsHandlers.cpp, plugins/.../Core/Subsystem/McpAutomationBridgeSubsystemSystemEditorRegistration.cpp
C++ HandleProjectSettingsAction handler extracts subAction and builds success responses; registered via MCP_REGISTER_DIRECT macro in system/editor handler init.
Project Settings Tool Schema and TypeScript Handler
plugins/.../MCP/Tools/Core/McpTool_ManageProjectSettings.cpp, src/tools/handlers/core/project-settings-handlers.ts
Tool definition exposes collision/channel/object/physics action schema; TypeScript handler dispatches to automation bridge with error handling.
Project Settings Tool Registration and Routing
src/tools/orchestration/consolidated-handler-registration.ts, src/tools/definitions/shared/all-tool-definitions.ts, src/utils/commands/action-constants.ts
Tool registered in handler registry, imported in all-tool-definitions, and added to action constants for discoverability.

Phase 34: Asset Workflow and Control Utilities Extensions

Layer / File(s) Summary
Asset Workflow Content Browser Handlers Implementation
plugins/.../Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowBrowser.cpp, plugins/.../Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowHandlers.cpp
Seven placeholder handler methods for navigation, sync, collections, coloring, explorer, and utility operations; dispatch routes additional content-browser actions.
Asset Workflow Handler Declarations and Declaration Updates
plugins/.../Public/McpAutomationBridgeSubsystemAssetWorkflowDeclarations.h
Handler declarations shift HandleRebuildMaterial from payload to action handler; add declarations for material details, navigation, collections, color, and utilities.
Manage Asset Tool Schema Extension
src/tools/definitions/core/manage-asset-tool.ts
Action enum expands with replace/reference, navigate, sync, collection, color, and utility actions; input schema adds filter, collectionName, color, and utilityPath properties.
Control Actor Selection and Grouping Implementation
plugins/.../Domains/ControlActor/McpAutomationBridge_ControlActorSelection.cpp, plugins/.../Domains/ControlActor/McpAutomationBridge_ControlActorSupport.h
Nine placeholder handlers for actor selection, class/tag/volume filtering, grouping, and utilities; only deselect performs real editor interaction.
Control Actor Dispatch Routing and Tool Schema Updates
plugins/.../Domains/ControlActor/McpAutomationBridge_ControlActorDispatch.cpp, src/tools/definitions/core/control-actor-tool.ts
Dispatch extends to route nine selection/grouping/utility subActions; tool schema adds volumeName, groupName, and utilityPath input parameters.
Control Editor Utilities and Grid/Snap/Layout Handlers
plugins/.../Domains/ControlEditor/McpAutomationBridge_ControlEditorUtilities.cpp, plugins/.../Domains/ControlEditor/McpAutomationBridge_ControlEditorDispatch.cpp, plugins/.../Domains/ControlEditor/McpAutomationBridge_ControlEditorSupport.h
Six placeholder handlers for grid/snap settings, layout, custom mode, and utility widget/task execution; dispatch and header declarations added.
Control Editor Tool Schema Extension
src/tools/definitions/core/control-editor-tool.ts
Action enum extends with grid/snap/layout/mode/widget/task operations; input schema adds gridSize, snapEnabled, layoutName, widgetPath, and taskPath.

Documentation, Testing, and Smoke Test Updates

Layer / File(s) Summary
Pull Request Documentation for Phase 31 and Phase 42
pull_request.md, pull_request_phase_42.md
Two PR description files document data/persistence and NPC plugin scopes with detailed file changes, testing notes, and pre-merge checklists.
Smoke Test and Integration Test Updates
scripts/smoke-test.ts, tests/mcp-tools/dynamic-handlers/toggle-fps.json
Smoke test updates expected tool count 23 → 25; toggle FPS test fixture added for dynamic handler testing.
Developer Test Scripts and Canonical Tool Surface Documentation
quick-test.mjs, run_dynamic_test.mjs, src/tools/AGENTS.md
Quick test script demonstrates widget authoring; dynamic test runner tests FPS/log handlers; canonical surface documentation lists new manage_project_settings tool.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • ChiR24/Unreal_mcp#429: Refactors manage_ai tool dispatcher to use McpConsolidatedActions::ManageAI() and modifies action enum structure; overlaps with this PR's Phase 42 NPC action/schema extensions in McpTool_ManageAI.cpp.

Suggested Labels

enhancement, size/xl, area/tools, area/server, area/docs, area/testing, area/plugin

Suggested Reviewers

  • ChiR24

Poem

🐰 The bridge grows stronger with each new deed,
NPC minds now think and heed,
Data flows through config gates,
While widgets sync at proper rates.
Tools multiply, the scope expands wide—
Automation's on a thrilling ride!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

⚠️ This pull request might be slop. It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pull_request.md (1)

1-41: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

PR documentation is incomplete for manage_project_settings tool.

This PR adds both manage_data and manage_project_settings tools (as evidenced by all-tool-definitions.ts and AGENTS.md), but pull_request.md only documents Phase 31 (manage_data).

The manage_project_settings tool is not mentioned in this PR documentation. If this file is scoped to Phase 31 only, ensure that manage_project_settings is documented in a separate phase document or in the main PR description.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pull_request.md` around lines 1 - 41, The PR document only describes Phase 31
/ manage_data but omits the new manage_project_settings tool; update
pull_request.md to either (A) add a section for manage_project_settings
mirroring the manage_data entries (summarize added TypeScript
definitions/dispatcher/registrations/native handlers/tests and reference
all-tool-definitions.ts and AGENTS.md where it’s registered) or (B) if
manage_project_settings belongs to a different phase, add a separate phase
document that documents those changes and reference it from this PR description;
ensure the symbol manage_project_settings is explicitly mentioned and its key
changes (TS schema, dispatcher, registry entries, native handler files, tests)
are listed.
src/tools/definitions/shared/all-tool-definitions.ts (1)

28-54: 📐 Maintainability & Code Quality | 🟠 Major

Fix canonical tool count mismatch (23 vs current 25)

src/tools/definitions/shared/all-tool-definitions.ts’s allToolDefinitions now contains 25 tool definitions (includes manageProjectSettingsToolDefinition and manageDataToolDefinition). But src/tools/AGENTS.md and other docs describe maintaining exactly 23 canonical public tool definitions, and consolidated-handler-registration.ts also registers 25 canonical tools.

Update the canonical-tool-list constraint/docs (and any tests expecting 23) to match the new intended public surface or revert the additional parent tool definitions/registrations to restore the 23-tool contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/definitions/shared/all-tool-definitions.ts` around lines 28 - 54,
The allToolDefinitions array now includes 25 entries (notably
manageProjectSettingsToolDefinition and manageDataToolDefinition), which breaks
the documented canonical count of 23 referenced in AGENTS.md and
consolidated-handler-registration.ts; either update the canonical
documentation/tests to expect 25 and adjust consolidated-handler-registration to
reflect the new public surface, or remove/revert the two added parent tool
definitions (manageProjectSettingsToolDefinition and manageDataToolDefinition)
and their registrations so allToolDefinitions and
consolidated-handler-registration.ts return to the original 23-tool
contract—locate and change the allToolDefinitions declaration, the
consolidated-handler-registration canonical registrations, and any
tests/AGENTS.md references accordingly.

Source: Coding guidelines

🟠 Major comments (19)
patch_cpp_feedback.py-4-4 (1)

4-4: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Hardcoded Windows-style paths in patch_cpp_feedback.py, patch_handlers.py, and patch_ts_handlers.py.

All three patch scripts use backslash (\) path separators, which fail on Linux/macOS. The root cause is the same: raw string paths instead of os.path.join(). Apply the fix consistently across patch_cpp_feedback.py (line 4), patch_handlers.py (line 3), and patch_ts_handlers.py (line 3).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patch_cpp_feedback.py` at line 4, Replace the hardcoded Windows-style raw
path assigned to repo_root in patch_cpp_feedback.py (and the equivalent root
variables in patch_handlers.py and patch_ts_handlers.py) with a cross-platform
construction using os.path.join (or pathlib.Path) so path separators are
platform-independent; specifically, import os (or pathlib), and change the
repo_root = r"plugins\McpAutomationBridge\Source\McpAutomationBridge\Private"
assignment to build the path via
os.path.join("plugins","McpAutomationBridge","Source","McpAutomationBridge","Private")
(or Path("plugins") / "McpAutomationBridge" / "Source" / "McpAutomationBridge" /
"Private") and apply the same change to the root variable names in
patch_handlers.py and patch_ts_handlers.py.
patch_cpp_feedback.py-45-77 (1)

45-77: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Silent failure when old function text doesn't match exactly.

The string replacement relies on exact whitespace and formatting. If the source file differs even slightly (trailing spaces, different indentation), the replacement silently does nothing and the script reports success.

Proposed fix: verify replacement occurred
 content = content.replace(old_apply_func, new_apply_func)
+
+if new_apply_func not in content:
+    raise RuntimeError("Failed to patch ApplyJsonStringToUmgAsset - function signature may have changed")

 with open(transform_file, "w", encoding="utf-8") as f:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patch_cpp_feedback.py` around lines 45 - 77, The replacement can silently
fail because content.replace requires an exact match; update the script that
swaps old_apply_func with new_apply_func to verify the replacement actually
occurred by checking for the presence of old_apply_func (or by comparing content
before/after) and if not found, attempt a fallback fuzzy match (e.g.
normalize/collapse whitespace or use a regex that matches
ApplyJsonStringToUmgAsset signature and body patterns) and if still not matched,
emit a clear error/diagnostic and exit non‑zero so CI fails; include the symbols
ApplyJsonStringToUmgAsset, old_apply_func and new_apply_func in your diagnostic
to help locate the location.
patch_handlers.py-14-45 (1)

14-45: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Silent skip when section marker is absent.

If '19.4 Advanced UMG JSON' is removed or renamed, the script silently skips all sanitization and GEditor guard patches. Consider failing explicitly or logging a warning.

Proposed fix
 section_start = content.find('19.4 Advanced UMG JSON')
-if section_start != -1:
+if section_start == -1:
+    raise RuntimeError("Section marker '19.4 Advanced UMG JSON' not found - handler file structure may have changed")
+else:
     section_content = content[section_start:]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patch_handlers.py` around lines 14 - 45, The code silently skips all patches
when the section marker '19.4 Advanced UMG JSON' is missing because
section_start == -1; change this behavior in the block that computes
section_start and uses content to either (a) emit a clear warning (e.g.,
logger.warning or print including the missing marker and file/context) and
continue, or (b) fail loudly by raising an exception or calling sys.exit(1) so
the caller/test harness notices the sanitization didn't run; update the logic
around section_start, the early-return/exit behavior, and any calling code that
expects the patches to have been applied (references: variable section_start,
section_content, pre_content and the subsequent replace operations for
GetJsonStringField and UUmgGetSubsystem/UUmgSetSubsystem) so missing marker is
explicitly reported rather than silently ignored.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlActor/McpAutomationBridge_ControlActorSelection.cpp-7-45 (1)

7-45: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not return success for unimplemented actor operations.

Line 7 through Line 98 currently return success=true for actions that perform no selection/grouping/utility work. Since control_actor routes live actions here, this creates false-positive automation results.

Suggested interim fix (fail clearly until implemented)
 bool UMcpAutomationBridgeSubsystem::HandleControlActorSelect(
     const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
     TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
-  TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
-  Result->SetBoolField(TEXT("success"), true);
-  Result->SetStringField(TEXT("message"), TEXT("Actor selected with camera focus."));
-  SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Selected"), Result);
+  SendStandardErrorResponse(this, RequestingSocket, RequestId, TEXT("NOT_IMPLEMENTED"),
+                            TEXT("select_actor is not implemented yet."), nullptr);
   return true;
 }

Also applies to: 60-98

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlActor/McpAutomationBridge_ControlActorSelection.cpp`
around lines 7 - 45, These handler functions (HandleControlActorSelect,
HandleControlActorSelectByClass, HandleControlActorSelectByTag,
HandleControlActorSelectInVolume) currently return success=true despite being
unimplemented; change each to return a clear failure response by setting the
Result->SetBoolField("success", false) and a descriptive
Result->SetStringField("message", e.g. "Not implemented: <function name>")
before calling SendAutomationResponse, so callers receive a deterministic
failure until real implementations are added. Ensure the RequestId,
RequestingSocket and the same response type ("Selected") are preserved when
sending the failure response.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowBrowser.cpp-8-10 (1)

8-10: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove duplicate success field from Result objects.

Each handler sets Result->SetBoolField("success", true), but SendAutomationResponse already communicates success via its bSuccess parameter. The Result object should contain only operation-specific data, not the top-level success indicator.

♻️ Proposed cleanup
   TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
-  Result->SetBoolField(TEXT("success"), true);
-  Result->SetStringField(TEXT("message"), TEXT("Navigated to path in Content Browser."));
   SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Navigated"), Result);

The message should be passed as the Message parameter to SendAutomationResponse, not embedded in the Result object.

Also applies to: 18-20, 28-30, 38-40, 48-50, 58-60, 68-70

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowBrowser.cpp`
around lines 8 - 10, Handlers are populating the Result FJsonObject with a
top-level "success" boolean and message string via Result->SetBoolField and
Result->SetStringField, but SendAutomationResponse already conveys success via
its bSuccess and Message parameters; remove the
Result->SetBoolField(TEXT("success"), true) and move the message out of Result
(do not set Result->SetStringField for the generic message) and instead pass the
human message into SendAutomationResponse's Message parameter while leaving
Result to contain only operation-specific fields (update the callers around
SendAutomationResponse to pass the message and keep Result for domain data).
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowBrowser.cpp-5-73 (1)

5-73: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add GEditor availability checks to all placeholder handlers.

All seven handlers lack GEditor availability checks. Content Browser operations require the editor to be available. As per coding guidelines, optional engine features must fail clearly when unavailable.

🛡️ Recommended pattern to add GEditor guard

Apply this pattern to each handler:

 bool UMcpAutomationBridgeSubsystem::HandleNavigateToPath(
     const FString &RequestId, const FString &Action, const TSharedPtr<FJsonObject> &Payload,
     TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
+  if (!GEditor) {
+    SendAutomationResponse(RequestingSocket, RequestId, false,
+                           TEXT("Editor not available"), nullptr, TEXT("EDITOR_UNAVAILABLE"));
+    return true;
+  }
   TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowBrowser.cpp`
around lines 5 - 73, Each of the placeholder handlers (HandleNavigateToPath,
HandleSyncToAsset, HandleCreateCollection, HandleAddToCollection,
HandleSetAssetColor, HandleShowInExplorer, HandleRunAssetActionUtility) must
guard against the editor being unavailable by checking GEditor before performing
work; add an early check at the top of each function (if GEditor is null) that
constructs a Result JSON with success=false and an explanatory message and calls
SendAutomationResponse(RequestingSocket, RequestId, false, TEXT("Editor
unavailable"), Result) and returns false so the caller gets a clear failure when
the engine/editor feature is absent.

Source: Coding guidelines

plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp-109-118 (1)

109-118: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Root cause: unsupported/unimplemented action paths report success in both McpAutomationBridge_DataHandlers.cpp and McpAutomationBridge_ProjectSettingsHandlers.cpp.
In plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp and plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Project/McpAutomationBridge_ProjectSettingsHandlers.cpp, stub/default branches return successful responses instead of explicit errors. This should be unified to fail clearly (UNKNOWN_ACTION / NOT_IMPLEMENTED) so transport and tests can reliably detect missing functionality.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp`
around lines 109 - 118, The default/stub branches currently return success for
unknown or unimplemented actions (e.g., the SubAction.StartsWith(...) branch and
the "Default handler" that call Subsystem->SendAutomationResponse(..., true,
...)); update these to return a failing response instead: call
Subsystem->SendAutomationResponse with success=false and supply a clear error
code/message such as "UNKNOWN_ACTION" or "NOT_IMPLEMENTED" (use your project's
existing error constants if present), then return false; apply the same change
pattern to both McpAutomationBridge_DataHandlers.cpp and
McpAutomationBridge_ProjectSettingsHandlers.cpp so transport/tests can detect
missing functionality.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp-16-20 (1)

16-20: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard against null payload before field access.

Payload is dereferenced without an IsValid() check. A malformed request path can crash this handler before an error response is sent.

Suggested fix
 Subsystem->RegisterHandler(TEXT("manage_data"), [Subsystem](const FString& RequestId, const FString& Action, const TSharedPtr<FJsonObject>& Payload, TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
+    if (!Payload.IsValid())
+    {
+        Subsystem->SendAutomationError(RequestingSocket, RequestId, TEXT("Missing payload object."), TEXT("MISSING_PARAMETER"));
+        return true;
+    }
     FString SubAction;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp`
around lines 16 - 20, The handler registered via Subsystem->RegisterHandler
dereferences Payload without checking validity; add a guard at the start of the
lambda that verifies Payload (e.g., if (!Payload.IsValid() or if (!Payload)) and
if invalid call Subsystem->SendAutomationError(RequestingSocket, RequestId,
TEXT("Missing payload."), TEXT("MISSING_PARAMETER")) and return early so
TryGetStringField and subsequent logic do not dereference a null Payload; ensure
the same pattern is used wherever Payload is accessed in this lambda.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp-30-57 (1)

30-57: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate required config fields before mutating config state.

configSection, configKey, and (for writes) configValue are optional in the current code path; empty values can trigger unintended reads/writes against ambiguous keys.

Suggested fix
 FString ConfigFilename, ConfigSection, ConfigKey, ConfigValue;
 Payload->TryGetStringField(TEXT("configFilename"), ConfigFilename);
-Payload->TryGetStringField(TEXT("configSection"), ConfigSection);
-Payload->TryGetStringField(TEXT("configKey"), ConfigKey);
-Payload->TryGetStringField(TEXT("configValue"), ConfigValue);
+const bool bHasSection = Payload->TryGetStringField(TEXT("configSection"), ConfigSection);
+const bool bHasKey = Payload->TryGetStringField(TEXT("configKey"), ConfigKey);
+const bool bHasValue = Payload->TryGetStringField(TEXT("configValue"), ConfigValue);
+if (!bHasSection || !bHasKey || !bHasValue || ConfigSection.IsEmpty() || ConfigKey.IsEmpty())
+{
+    Subsystem->SendAutomationError(RequestingSocket, RequestId, TEXT("Missing required config fields."), TEXT("MISSING_PARAMETER"));
+    return true;
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp`
around lines 30 - 57, The read/write config handlers must validate required
fields before calling GConfig; check that ConfigSection and ConfigKey are
non-empty for "read_config_value" and that ConfigSection, ConfigKey, and
ConfigValue are non-empty for "write_config_value" (use the local variables
ConfigSection, ConfigKey, ConfigValue extracted from Payload), and if any
required field is empty call Subsystem->SendAutomationError(RequestingSocket,
RequestId, TEXT("Missing required config field(s)"), TEXT("INVALID_ARGUMENT"))
and return false without calling GConfig->GetString/SetString/Flush; keep
existing success/error paths unchanged otherwise.
src/tools/AGENTS.md-32-35 (1)

32-35: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Gameplay section is missing manage_data tool.

The manage_data tool (defined in src/tools/definitions/gameplay/manage-data-tool.ts with category: 'gameplay') was added to all-tool-definitions.ts but is not documented in the Gameplay section of the canonical surface.

The Gameplay line should read:

- Gameplay: `animation_physics`, `manage_effect`, `manage_gas`, `manage_character`, `manage_combat`, `manage_ai`, `manage_inventory`, `manage_interaction`, `manage_data`.
📝 Proposed fix
 ## CANONICAL SURFACE
 - Core: `manage_tools`, `manage_asset`, `manage_blueprint`, `control_actor`, `control_editor`, `manage_project_settings`, `manage_level`, `system_control`, `inspect`.
 - World: `build_environment`, `manage_geometry`, `manage_pcg`, `manage_level_structure`.
-- Gameplay: `animation_physics`, `manage_effect`, `manage_gas`, `manage_character`, `manage_combat`, `manage_ai`, `manage_inventory`, `manage_interaction`.
+- Gameplay: `animation_physics`, `manage_effect`, `manage_gas`, `manage_character`, `manage_combat`, `manage_ai`, `manage_inventory`, `manage_interaction`, `manage_data`.
 - Utility: `manage_sequence`, `manage_audio`, `manage_networking`.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/AGENTS.md` around lines 32 - 35, Update the Gameplay list in
AGENTS.md to include the missing manage_data tool; find the Gameplay line
currently listing `animation_physics`, `manage_effect`, `manage_gas`,
`manage_character`, `manage_combat`, `manage_ai`, `manage_inventory`,
`manage_interaction` and append `manage_data` so it reads: `animation_physics`,
`manage_effect`, `manage_gas`, `manage_character`, `manage_combat`, `manage_ai`,
`manage_inventory`, `manage_interaction`, `manage_data`. Confirm this matches
the tool definition for manage_data (the manage-data-tool module) and that
all-tool-definitions includes the same symbol so docs and code stay in sync.

Source: Coding guidelines

src/tools/handlers/data/data-handlers.ts-6-6 (1)

6-6: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Avoid Promise<any> in runtime handler signatures.

On Line 6, Promise<any> bypasses strict typing for a core dispatch boundary. Return a concrete shape (e.g., Promise<Record<string, unknown>> or shared response type) to preserve contract safety.

As per coding guidelines, “Keep TypeScript strict.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/handlers/data/data-handlers.ts` at line 6, Change the handler
signature for handleDataTools to return a concrete, strictly typed shape instead
of Promise<any>; update the function signature (handleDataTools) to return
Promise<Record<string, unknown>> or a shared response interface (e.g.,
DataHandlerResponse) and ensure all return paths and any callers are adjusted to
produce/expect that structure so the dispatch boundary is strictly typed.

Source: Coding guidelines

src/tools/handlers/core/project-settings-handlers.ts-2-17 (1)

2-17: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Avoid custom catch-envelope in handler responses.

On Line 11, this handler returns a bespoke { success, message, error } shape from catch, which diverges from the shared handler contract and can break uniform downstream error handling. Prefer letting executeAutomationRequest(...)/shared helpers propagate structured errors consistently.

As per coding guidelines, “Clean or promote bridge results through existing helpers; do not invent a second response envelope in TypeScript handlers.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/handlers/core/project-settings-handlers.ts` around lines 2 - 17,
The handler handleProjectSettingsTools is catching errors and returning a custom
{ success, message, error } envelope which breaks the shared handler contract;
remove the custom catch and let executeAutomationRequest(...) propagate its
structured errors (either delete the try/catch or rethrow the caught error with
throw error) so callers receive the standard error shape from the shared
helpers, keeping the function body to build payload, await
executeAutomationRequest(tools, 'manage_project_settings', payload), and return
that result as Record<string, unknown>.

Source: Coding guidelines

scripts/smoke-test.ts-18-18 (1)

18-18: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tool-count assertion conflicts with canonical tool-list contract.

On Line 18, asserting totalTools: 25 conflicts with the canonical requirement to maintain exactly 23 public tool definitions. This smoke assertion should enforce the canonical count, not codify drift.

As per coding guidelines, “Maintain exactly 23 tool definitions in public list order in the canonical tool list.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/smoke-test.ts` at line 18, The smoke test currently asserts
totalTools: z.literal(25) which hardcodes an incorrect tool count; change the
assertion to expect the canonical 23 tools by replacing the literal 25 with 23
(i.e., update the totalTools literal used in the smoke-test schema), and verify
the test uses the totalTools identifier consistently so it will fail if the
public tool list diverges from the required 23 entries.

Source: Coding guidelines

tests/mcp-tools/gameplay/manage-data.test.mjs-48-48 (1)

48-48: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not use broad expectation masks in MCP tool tests.

On Line 48, expected: 'success|error' allows contradictory outcomes and can mask regressions. Split this into explicit primary-case expectations (or use allowed narrow alternatives per rule).

As per coding guidelines, “Do not use broad expectation masks such as success|error…”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/mcp-tools/gameplay/manage-data.test.mjs` at line 48, The test uses a
broad expectation mask on the test case's expected property (`expected:
'success|error'`) which hides regressions; replace this with an explicit primary
expected outcome (e.g., `expected: 'success'`) and if multiple outcomes are
legitimately allowed, enumerate them narrowly (e.g., `allowed:
['success','not_found']`) or split into separate test cases for the success and
error paths; update the test case that contains the `expected` field in
manage-data.test.mjs (or add a separate test name for the error case) so
assertions check a single clear outcome or a small explicit set rather than the
mask `'success|error'`.

Source: Coding guidelines

src/tools/handlers/core/project-settings-handlers.ts-2-2 (1)

2-2: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace tools: any with the shared tools interface.

On Line 2, tools: any weakens strict handler typing and obscures dispatch-contract mistakes at compile time. Use ITools (and the standard handler arg type) here.

As per coding guidelines, “Use Strict NodeNext TypeScript without as any, @ts-ignore…” and “Keep TypeScript strict.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/handlers/core/project-settings-handlers.ts` at line 2, The function
handleProjectSettingsTools currently types the third param as tools: any;
replace that with the shared tools interface ITools and use the standard handler
arg type for args (e.g., HandlerArgs or the project’s canonical handler arg
type) so the signature becomes something like handleProjectSettingsTools(action:
string, args: HandlerArgs, tools: ITools): Promise<Record<string, unknown>>; add
the appropriate imports for ITools and the handler arg type from the shared
types module, update any internal usages that assumed any to match the stricter
types, and run the TypeScript build to fix any resulting type errors and adjust
callers if necessary.

Source: Coding guidelines

plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlEditor/McpAutomationBridge_ControlEditorUtilities.cpp-6-64 (1)

6-64: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate input parameters in placeholder handlers.

All six handlers ignore the Payload parameter entirely and return hardcoded success responses without validating any input. Even placeholder implementations should validate that required parameters are present and well-formed, or fail with a clear error message.

For example:

  • HandleControlEditorSetGridSettings should validate gridSize is present and is a positive number
  • HandleControlEditorSetSnapSettings should validate snapEnabled is present and is a boolean
  • HandleControlEditorManageLayouts should validate layoutName is present and non-empty
  • HandleControlEditorSpawnUtilityWidget and HandleControlEditorRunUtilityTask should validate and normalize asset paths (widgetPath, taskPath)

As per coding guidelines, all external input must be validated. The current implementations will succeed for malformed requests, which could mask integration issues during testing.

🛡️ Example validation for HandleControlEditorSetGridSettings
 bool UMcpAutomationBridgeSubsystem::HandleControlEditorSetGridSettings(
     const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
     TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
+  double GridSize = 0.0;
+  if (!Payload->TryGetNumberField(TEXT("gridSize"), GridSize) || GridSize <= 0.0) {
+    SendStandardErrorResponse(this, RequestingSocket, RequestId, TEXT("INVALID_PARAMETER"),
+                              TEXT("gridSize must be a positive number"), nullptr);
+    return true;
+  }
+
   TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
   Result->SetBoolField(TEXT("success"), true);
-  Result->SetStringField(TEXT("message"), TEXT("Grid settings applied."));
+  Result->SetStringField(TEXT("message"), FString::Printf(TEXT("Grid settings applied: size=%.2f"), GridSize));
   SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Grid settings updated"), Result);
   return true;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlEditor/McpAutomationBridge_ControlEditorUtilities.cpp`
around lines 6 - 64, All six handlers currently ignore Payload and return
hardcoded success; update each handler (HandleControlEditorSetGridSettings,
HandleControlEditorSetSnapSettings, HandleControlEditorManageLayouts,
HandleControlEditorCreateCustomMode, HandleControlEditorSpawnUtilityWidget,
HandleControlEditorRunUtilityTask) to parse and validate required fields from
Payload, returning an error SendAutomationResponse when validation fails: for
HandleControlEditorSetGridSettings validate a numeric "gridSize" > 0; for
HandleControlEditorSetSnapSettings validate a boolean "snapEnabled"; for
HandleControlEditorManageLayouts validate a non-empty string "layoutName"; for
HandleControlEditorCreateCustomMode validate any required mode fields (e.g.,
non-empty "modeName"); for HandleControlEditorSpawnUtilityWidget validate and
normalize a non-empty string "widgetPath" (ensure valid asset path format); for
HandleControlEditorRunUtilityTask validate and normalize a non-empty string
"taskPath"; on failure populate Result JSON with success=false and a clear
message and call SendAutomationResponse(RequestingSocket, RequestId, false,
TEXT("..."), Result); on success continue to set success=true and appropriate
message as before.

Source: Coding guidelines

src/tools/definitions/core/control-editor-tool.ts-28-29 (1)

28-29: 🔒 Security & Privacy | 🟠 Major

Add per-action parameter validation for the Phase 34 editor utility actions
set_grid_settings, set_snap_settings, manage_editor_layouts, create_custom_editor_mode, spawn_editor_utility_widget, and run_editor_utility_task are missing from both ACTION_REQUIRED_PARAMS and ACTION_ALLOWED_PARAMS in src/tools/handlers/editor/editor-action-validation.ts. As a result, validateEditorActionArgs skips required/allowed parameter enforcement for these actions (it only runs validateArgsSecurity), so malformed/missing/extra parameters can pass through to the C++ bridge.

Add entries mapping each action to its required/allowed parameters, using the parameter names already defined in src/tools/definitions/core/control-editor-tool.ts:

  • set_grid_settingsgridSize
  • set_snap_settingssnapEnabled
  • manage_editor_layoutslayoutName
  • create_custom_editor_modemode
  • spawn_editor_utility_widgetwidgetPath
  • run_editor_utility_tasktaskPath
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/definitions/core/control-editor-tool.ts` around lines 28 - 29, Add
per-action parameter validation entries for the six Phase 34 editor utility
actions in the ACTION_REQUIRED_PARAMS and ACTION_ALLOWED_PARAMS maps inside
validateEditorActionArgs (file: editor-action-validation.ts). Specifically, add
keys for 'set_grid_settings', 'set_snap_settings', 'manage_editor_layouts',
'create_custom_editor_mode', 'spawn_editor_utility_widget', and
'run_editor_utility_task' and map them to their required/allowed parameter names
as defined in control-editor-tool.ts: set_grid_settings → 'gridSize',
set_snap_settings → 'snapEnabled', manage_editor_layouts → 'layoutName',
create_custom_editor_mode → 'mode', spawn_editor_utility_widget → 'widgetPath',
run_editor_utility_task → 'taskPath'; ensure these entries appear in both
ACTION_REQUIRED_PARAMS and ACTION_ALLOWED_PARAMS so validateEditorActionArgs
enforces required and allowed param checks for those actions.

Source: Coding guidelines

plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp-9-143 (1)

9-143: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Missing runtime GEditor check across all 24 Phase 42 NPC handlers in McpAutomationBridge_NPCBehaviorModes.cpp, McpAutomationBridge_NPCDialogue.cpp, McpAutomationBridge_NPCDirector.cpp, and McpAutomationBridge_NPCMemory.cpp.

All 24 new NPC handlers use WITH_EDITOR (compile-time) without a runtime GEditor != nullptr check. Per coding guidelines for Private/Core/**/*.cpp and Private/Domains/**/*.cpp, editor API work should validate editor availability at runtime. The shared root cause is the missing runtime guard pattern across all four NPC implementation files.

🛡️ Recommended runtime guard for all NPC handlers

Add at the start of each WITH_EDITOR block in all 24 handlers:

`#if` WITH_EDITOR
    if (!GEditor)
    {
        Self->SendAutomationError(RequestingSocket, RequestId,
            TEXT("Editor not available"), TEXT("EDITOR_NOT_AVAILABLE"));
        return true;
    }
    // ... existing handler logic
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp`
around lines 9 - 143, All NPC handler functions (e.g., HandleSetupPatrolMode,
HandleSetupAlertMode, HandleSetupCombatMode, HandleSetupIdleMode,
HandleConfigureModeTransitions, HandleAddPatrolWaypoint and the other 18
handlers across the NPC behavior/dialogue/director/memory files) currently rely
only on `#if` WITH_EDITOR; add a runtime editor availability guard at the start of
each WITH_EDITOR block: check if GEditor is null, and if so call
Self->SendAutomationError(RequestingSocket, RequestId, TEXT("Editor not
available"), TEXT("EDITOR_NOT_AVAILABLE")) and return true; leave the existing
handler logic unchanged after this guard so editor-only APIs are only used when
GEditor is valid.

Source: Coding guidelines

src/tools/definitions/gameplay/ai/manage-ai-npc-properties.ts-132-149 (1)

132-149: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Enforce documented numeric bounds in the schema.

Line 135-Line 149 describe strict ranges, but the schema does not enforce them. Out-of-range values can pass validation and reach native handlers.

Suggested patch
   personalityTraits: {
     type: 'object',
     properties: {
-      aggression: { type: 'number', description: '0.0 - 1.0' },
-      curiosity: { type: 'number', description: '0.0 - 1.0' },
-      cowardice: { type: 'number', description: '0.0 - 1.0' },
-      loyalty: { type: 'number', description: '0.0 - 1.0' }
+      aggression: { type: 'number', minimum: 0, maximum: 1, description: '0.0 - 1.0' },
+      curiosity: { type: 'number', minimum: 0, maximum: 1, description: '0.0 - 1.0' },
+      cowardice: { type: 'number', minimum: 0, maximum: 1, description: '0.0 - 1.0' },
+      loyalty: { type: 'number', minimum: 0, maximum: 1, description: '0.0 - 1.0' }
     },
     description: 'NPC personality trait values (0.0 = low, 1.0 = high).'
   },
@@
   reputationScore: {
     type: 'number',
+    minimum: -100,
+    maximum: 100,
     description: 'Initial reputation score with the faction (-100 to 100).'
   }

As per coding guidelines: “Validate all external input: command strings go through CommandValidator and paths go through normalization/security helpers.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/definitions/gameplay/ai/manage-ai-npc-properties.ts` around lines
132 - 149, The schema currently documents numeric bounds but does not enforce
them; update the property schemas so the personalityTraits sub-properties
(aggression, curiosity, cowardice, loyalty) include minimum: 0 and maximum: 1,
and add minimum: -100 and maximum: 100 to reputationScore, ensuring they remain
type: 'number' so out-of-range values are rejected by validation (locate the
personalityTraits object and the reputationScore property to apply these
changes).

Source: Coding guidelines

🟡 Minor comments (3)
src/tools/definitions/gameplay/manage-data-tool.ts-48-61 (1)

48-61: 📐 Maintainability & Code Quality | 🟡 Minor

Remove duplicate success field from manage-data-tool output schema: commonSchemas.outputBase already defines success: { type: 'boolean' }, so the explicit success: commonSchemas.booleanProp in src/tools/definitions/gameplay/manage-data-tool.ts (lines 48-61) is redundant and should be dropped.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/definitions/gameplay/manage-data-tool.ts` around lines 48 - 61, The
outputSchema in manage-data-tool currently duplicates the success property:
remove the explicit success: commonSchemas.booleanProp entry from the
outputSchema so it relies on success defined in commonSchemas.outputBase; update
any tests or usages expecting the duplicate key if needed and keep the rest of
properties (assetPath, rowName, slotName, tagName, configValue, exists, error)
intact to avoid changing shape otherwise.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/McpAutomationBridge_AIHandlerContext.h-77-101 (1)

77-101: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Action count inconsistency across PR description, tool comment, and implementation.

The PR objectives state "22 new actions", and the tool definition comment (McpTool_ManageAI.cpp:1) claims "82 actions" total, but this header declares 24 new handlers (6 dialogue + 6 behavior modes + 6 director + 6 memory). If the original count was 60, that yields 84 total actions, not 82.

Please reconcile the action counts across the PR description, tool definition comment, and actual handler declarations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/McpAutomationBridge_AIHandlerContext.h`
around lines 77 - 101, The handler declarations added in
McpAutomationBridge_AIHandlerContext.h using MCP_AI_HANDLER_DECL (e.g.,
HandleCreateNPCDialogueTree ... HandleGetNPCInfo) total 24, which conflicts with
the PR/Tool comment counts (PR says +22, McpTool_ManageAI.cpp comment says total
82 while previous total was 60). Reconcile by either updating the tool comment
and PR description to reflect the actual +24/new total (60+24=84) or by
adjusting the declarations to match the intended +22 (remove or merge the two
extra handlers); ensure the final counts in McpTool_ManageAI.cpp comment, the PR
description, and the MCP_AI_HANDLER_DECL list in
McpAutomationBridge_AIHandlerContext.h are consistent.
pull_request_phase_42.md-35-38 (1)

35-38: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced code block.

Line 35 should specify a language (for example bash) to satisfy markdown linting.

Suggested patch
-```
+```bash
 npm run build:core   ✅  No TypeScript errors
 npm run test:smoke   ✅  25 tools detected
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @pull_request_phase_42.md around lines 35 - 38, The fenced code block
containing the npm commands lacks a language tag, which fails markdown linting;
update the block in pull_request_phase_42.md that shows "npm run build:core ✅
No TypeScript errors" / "npm run test:smoke ✅ 25 tools detected" to include a
language identifier (e.g., "bash") after the opening backticks so the block
becomes a properly tagged code fence.


</details>

<!-- cr-comment:v1:950a143d81205fc2ca29c360 -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>patch_ts_handlers.py (1)</summary><blockquote>

`49-58`: _📐 Maintainability & Code Quality_ | _⚡ Quick win_

**Loop variable captured by closure (B023).**

The `repl` function captures `action` by reference. While safe here because `re.sub` executes immediately, this pattern is fragile and triggers static analysis warnings. Bind explicitly to avoid future refactoring bugs.





<details>
<summary>Proposed fix using default argument binding</summary>

```diff
 for action in actions_to_patch:
     # Find the case block
     pattern = rf"(case '{action}': {{\s*requireNonEmptyString\(argsRecord\.widgetPath, 'widgetPath', 'Missing required parameter: widgetPath'\);(.*?)\s*return sendRequest\('{action}'\);\s*}})"
     
-    def repl(m):
+    def repl(m, action=action):
         inner = m.group(2)
         new_inner = inner + "\n      argsRecord.widgetPath = sanitizePath(argsRecord.widgetPath as string);\n"
         return f"case '{action}': {{\n      requireNonEmptyString(argsRecord.widgetPath, 'widgetPath', 'Missing required parameter: widgetPath');{new_inner}      return sendRequest('{action}');\n    }}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patch_ts_handlers.py` around lines 49 - 58, The inner function repl currently
captures the loop variable action by reference which can cause B023 warnings and
fragile behavior; modify repl to bind action as a default parameter (e.g., def
repl(m, action=action): ...) or create a small factory that returns a repl
function closed over the current action, then call re.sub(pattern, repl,
content, flags=re.DOTALL) as before so each replacement uses the correct action
value for actions_to_patch.

Source: Linters/SAST tools

patch_cpp_feedback.py (1)

7-13: 🩺 Stability & Availability | ⚡ Quick win

No error handling for missing files.

If any target file is missing or renamed, the script crashes with an unhelpful traceback. Adding explicit checks improves debuggability during CI failures.

Proposed improvement
 handlers_file = os.path.join(repo_root, "McpAutomationBridge_WidgetAuthoringHandlers.cpp")
+if not os.path.exists(handlers_file):
+    raise FileNotFoundError(f"Expected file not found: {handlers_file}")
 with open(handlers_file, "r", encoding="utf-8") as f:

Apply similarly to get_sub_file, set_sub_file, and transform_file.

Also applies to: 17-22, 26-31, 35-36

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patch_cpp_feedback.py` around lines 7 - 13, The code reads and writes files
(e.g., handlers_file) without any presence checks or exception handling, causing
crashes when files are missing; update the logic around open(...) for
handlers_file (and similarly in get_sub_file, set_sub_file, transform_file) to
first verify the file exists (os.path.exists or pathlib.Path.exists) and/or wrap
reads/writes in try/except to catch FileNotFoundError/IOError, log a clear error
message including the filename, and skip or fail gracefully instead of letting
an unhandled traceback propagate.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp (1)

4-4: 📐 Maintainability & Code Quality | 💤 Low value

Remove unused Serialization/JsonWriter.h include.

This file includes JsonWriter.h but does not use TJsonWriter or related serialization classes. The JSON response is constructed via McpHandlerUtils::CreateResultObject() which is already available through the AIHandlerContext.h include.

♻️ Proposed fix
 `#include` "Domains/AI/McpAutomationBridge_AIHandlerContext.h"
-#include "Serialization/JsonWriter.h"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp`
at line 4, Remove the unused include "Serialization/JsonWriter.h" from
McpAutomationBridge_NPCBehaviorModes.cpp; the file does not use TJsonWriter or
related serialization types and instead builds JSON via
McpHandlerUtils::CreateResultObject (available through AIHandlerContext.h), so
simply delete the `#include` "Serialization/JsonWriter.h" line to eliminate the
unnecessary dependency.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3d451ce9-31d5-4a2a-b5ee-cd5abff159f9

📥 Commits

Reviewing files that changed from the base of the PR and between af185be and efca8c9.

📒 Files selected for processing (49)
  • patch_cpp_feedback.py
  • patch_handlers.py
  • patch_ts_handlers.py
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Core/Subsystem/McpAutomationBridgeSubsystemHandlerRegistration.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Core/Subsystem/McpAutomationBridgeSubsystemSystemEditorRegistration.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/McpAutomationBridge_AIHandlerContext.h
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/McpAutomationBridge_AIHandlers.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCBehaviorModes.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCDialogue.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCDirector.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AI/NPC/McpAutomationBridge_NPCMemory.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowBrowser.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowHandlers.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlActor/McpAutomationBridge_ControlActorDispatch.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlActor/McpAutomationBridge_ControlActorSelection.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlActor/McpAutomationBridge_ControlActorSupport.h
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlEditor/McpAutomationBridge_ControlEditorDispatch.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlEditor/McpAutomationBridge_ControlEditorSupport.h
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlEditor/McpAutomationBridge_ControlEditorUtilities.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Data/McpAutomationBridge_DataHandlers.h
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/Project/McpAutomationBridge_ProjectSettingsHandlers.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/MCP/Routing/McpConsolidatedActionRoutingAI.h
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/MCP/Tools/Core/McpTool_ManageProjectSettings.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/MCP/Tools/Gameplay/McpTool_ManageAI.cpp
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystemAssetWorkflowDeclarations.h
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystemDomainRoutingDeclarations.h
  • pull_request.md
  • pull_request_phase_42.md
  • quick-test.mjs
  • run_dynamic_test.mjs
  • scripts/smoke-test.ts
  • src/tools/AGENTS.md
  • src/tools/definitions/core/control-actor-tool.ts
  • src/tools/definitions/core/control-editor-tool.ts
  • src/tools/definitions/core/manage-asset-tool.ts
  • src/tools/definitions/core/manage-project-settings-tool.ts
  • src/tools/definitions/gameplay/ai/manage-ai-behavior-properties.ts
  • src/tools/definitions/gameplay/ai/manage-ai-input-schema.ts
  • src/tools/definitions/gameplay/ai/manage-ai-npc-properties.ts
  • src/tools/definitions/gameplay/manage-data-tool.ts
  • src/tools/definitions/shared/all-tool-definitions.ts
  • src/tools/handlers/core/project-settings-handlers.ts
  • src/tools/handlers/data/data-handlers.ts
  • src/tools/orchestration/consolidated-handler-registration.ts
  • src/tools/orchestration/consolidated-routing.ts
  • src/utils/commands/action-constants.ts
  • tests/mcp-tools/dynamic-handlers/toggle-fps.json
  • tests/mcp-tools/gameplay/manage-data.test.mjs

Comment on lines +1 to +64
#include "Domains/ControlEditor/McpAutomationBridge_ControlEditorSupport.h"

// Placeholder implementations for the Phase 34 Utilities.
// Detailed APIs for layout management, Editor Utility widgets, etc. will be mapped here.

bool UMcpAutomationBridgeSubsystem::HandleControlEditorSetGridSettings(
const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetBoolField(TEXT("success"), true);
Result->SetStringField(TEXT("message"), TEXT("Grid settings applied."));
SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Grid settings updated"), Result);
return true;
}

bool UMcpAutomationBridgeSubsystem::HandleControlEditorSetSnapSettings(
const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetBoolField(TEXT("success"), true);
Result->SetStringField(TEXT("message"), TEXT("Snap settings applied."));
SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Snap settings updated"), Result);
return true;
}

bool UMcpAutomationBridgeSubsystem::HandleControlEditorManageLayouts(
const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetBoolField(TEXT("success"), true);
Result->SetStringField(TEXT("message"), TEXT("Editor layout updated."));
SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Layout managed"), Result);
return true;
}

bool UMcpAutomationBridgeSubsystem::HandleControlEditorCreateCustomMode(
const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetBoolField(TEXT("success"), true);
Result->SetStringField(TEXT("message"), TEXT("Custom editor mode created."));
SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Mode created"), Result);
return true;
}

bool UMcpAutomationBridgeSubsystem::HandleControlEditorSpawnUtilityWidget(
const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetBoolField(TEXT("success"), true);
Result->SetStringField(TEXT("message"), TEXT("Editor utility widget spawned."));
SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Widget spawned"), Result);
return true;
}

bool UMcpAutomationBridgeSubsystem::HandleControlEditorRunUtilityTask(
const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetBoolField(TEXT("success"), true);
Result->SetStringField(TEXT("message"), TEXT("Editor utility task executed."));
SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Task executed"), Result);
return true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Wrap implementations in #if WITH_EDITOR guard.

The entire file defines editor-only handler methods on UMcpAutomationBridgeSubsystem without a #if WITH_EDITOR guard. The corresponding header declarations (in McpAutomationBridge_ControlEditorSupport.h line 95-105) and dispatch branches (in McpAutomationBridge_ControlEditorDispatch.cpp line 22) are both inside #if WITH_EDITOR blocks, but these implementations are not.

This will cause compilation or link errors in non-editor builds when the linker tries to resolve these method definitions. As per coding guidelines, optional engine features must compile away or fail clearly when unavailable.

🐛 Proposed fix to add editor guard
 `#include` "Domains/ControlEditor/McpAutomationBridge_ControlEditorSupport.h"
 
+#if WITH_EDITOR
+
 // Placeholder implementations for the Phase 34 Utilities.
 // Detailed APIs for layout management, Editor Utility widgets, etc. will be mapped here.
 
 bool UMcpAutomationBridgeSubsystem::HandleControlEditorSetGridSettings(
     const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
     TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
   TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
   Result->SetBoolField(TEXT("success"), true);
   Result->SetStringField(TEXT("message"), TEXT("Grid settings applied."));
   SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Grid settings updated"), Result);
   return true;
 }
 
 // ... remaining handlers ...
 
 bool UMcpAutomationBridgeSubsystem::HandleControlEditorRunUtilityTask(
     const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
     TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
   TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
   Result->SetBoolField(TEXT("success"), true);
   Result->SetStringField(TEXT("message"), TEXT("Editor utility task executed."));
   SendAutomationResponse(RequestingSocket, RequestId, true, TEXT("Task executed"), Result);
   return true;
 }
+
+#endif // WITH_EDITOR
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/ControlEditor/McpAutomationBridge_ControlEditorUtilities.cpp`
around lines 1 - 64, The editor-only handler implementations for
UMcpAutomationBridgeSubsystem (HandleControlEditorSetGridSettings,
HandleControlEditorSetSnapSettings, HandleControlEditorManageLayouts,
HandleControlEditorCreateCustomMode, HandleControlEditorSpawnUtilityWidget,
HandleControlEditorRunUtilityTask) must be compiled only in editor builds; wrap
the entire implementations block in `#if` WITH_EDITOR / `#endif` so they match the
header declarations and dispatch branches that are already inside `#if`
WITH_EDITOR, preventing unresolved symbol/link errors in non-editor builds.

Source: Coding guidelines

Comment thread run_dynamic_test.mjs
Comment on lines +1 to +8
import { runToolTests } from './tests/test-runner.mjs';

const testCases = [
{ scenario: 'Test dynamic handler toggle_fps', toolName: 'toggle_fps', arguments: {}, expected: 'success' },
{ scenario: 'Test dynamic handler clear_logs', toolName: 'clear_logs', arguments: {}, expected: 'success' }
];

runToolTests('DynamicHandlers', testCases).catch(console.error);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if toggle_fps and clear_logs appear in canonical definitions
rg -n 'toggle_fps|clear_logs' src/tools/definitions/shared/all-tool-definitions.ts

# Check handler registrations
rg -n 'toggle_fps|clear_logs' src/tools/orchestration/consolidated-handler-registration.ts

# Count total defined tools (should be 23 baseline, or 25 if these two were added)
rg 'export const allToolDefinitions' -A 100 src/tools/definitions/shared/all-tool-definitions.ts | rg 'toolName:' | wc -l

Repository: ChiR24/Unreal_mcp

Length of output: 63


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find any occurrences of the tool names anywhere in src/
echo "=== occurrences of toggle_fps ==="
rg -n "toggle_fps" src || true

echo "=== occurrences of clear_logs ==="
rg -n "clear_logs" src || true

# 2) Check whether the referenced files exist
echo "=== file existence ==="
ls -la src/tools/definitions/shared/all-tool-definitions.ts || true
ls -la src/tools/orchestration/consolidated-handler-registration.ts || true

# 3) If they exist, show relevant excerpts around the regex match (if any)
echo "=== all-tool-definitions excerpt for toolName ==="
rg -n "export const allToolDefinitions" src/tools/definitions/shared/all-tool-definitions.ts || true
rg -n "toolName:" src/tools/definitions/shared/all-tool-definitions.ts | head -n 50 || true

echo "=== consolidated-handler-registration excerpt ==="
rg -n "toggle_fps|clear_logs" src/tools/orchestration/consolidated-handler-registration.ts || true
rg -n "toolName:" src/tools/orchestration/consolidated-handler-registration.ts | head -n 50 || true

# 4) Count toolName occurrences more robustly
echo "=== counts ==="
if [ -f src/tools/definitions/shared/all-tool-definitions.ts ]; then
  rg -n "toolName:" src/tools/definitions/shared/all-tool-definitions.ts | wc -l
fi

Repository: ChiR24/Unreal_mcp

Length of output: 539


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== search for toolName key in all-tool-definitions ==="
rg -n "toolName" src/tools/definitions/shared/all-tool-definitions.ts || true
rg -n "name:" src/tools/definitions/shared/all-tool-definitions.ts || true
rg -n "id:" src/tools/definitions/shared/all-tool-definitions.ts || true

echo "=== search for toggle/clear variants in all-tool-definitions ==="
rg -n -i "toggle[_-]?fps|togglefps|clear[_-]?logs|clearlogs" src/tools/definitions/shared/all-tool-definitions.ts || true

echo "=== search for toggle/clear variants anywhere in src (case-insensitive) ==="
rg -n -i "toggle[_-]?fps|togglefps|clear[_-]?logs|clearlogs" src || true

echo "=== show beginning of all-tool-definitions (to learn schema) ==="
cat -n src/tools/definitions/shared/all-tool-definitions.ts | head -n 200

echo "=== show beginning of consolidated-handler-registration (to learn schema) ==="
cat -n src/tools/orchestration/consolidated-handler-registration.ts | head -n 200

echo "=== show any exports/registration maps in consolidated-handler-registration ==="
rg -n "register|handler|tool|consolidated" src/tools/orchestration/consolidated-handler-registration.ts || true

Repository: ChiR24/Unreal_mcp

Length of output: 27187


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== repo-wide occurrences of toggle_fps/clear_logs ==="
rg -n "toggle_fps|clear_logs" . || true

echo "=== locate run_dynamic_test.mjs and show relevant section ==="
ls -la run_dynamic_test.mjs || true
if [ -f run_dynamic_test.mjs ]; then
  cat -n run_dynamic_test.mjs | head -n 120
fi

echo "=== inspect tests/test-runner.mjs ==="
ls -la tests/test-runner.mjs || true
if [ -f tests/test-runner.mjs ]; then
  wc -l tests/test-runner.mjs
  cat -n tests/test-runner.mjs | head -n 220
fi

echo "=== inspect any dynamic handler registry code ==="
# If there is a dynamic handler registry referenced earlier, inspect it
ls -la src/tools/orchestration/dynamic-handler-registry.ts || true
ls -la src/tools/orchestration/dynamic-handler-registry.js || true
if [ -f src/tools/orchestration/dynamic-handler-registry.ts ]; then
  wc -l src/tools/orchestration/dynamic-handler-registry.ts
  cat -n src/tools/orchestration/dynamic-handler-registry.ts | head -n 220
fi
if [ -f src/tools/orchestration/dynamic-handler-registry.js ]; then
  wc -l src/tools/orchestration/dynamic-handler-registry.js
  cat -n src/tools/orchestration/dynamic-handler-registry.js | head -n 220
fi

Repository: ChiR24/Unreal_mcp

Length of output: 11277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show runToolTests implementation + call sites for toolName
rg -n "function runToolTests|runToolTests\\(" tests/test-runner.mjs

echo "=== dump around runToolTests definition ==="
rg -n "runToolTests" tests/test-runner.mjs | head -n 20

# Use sed to print around the definition (first match)
line=$(rg -n "export function runToolTests" tests/test-runner.mjs | cut -d: -f1 | head -n1 || true)
if [ -n "${line:-}" ]; then
  start=$((line-60))
  end=$((line+220))
  sed -n "${start},${end}p" tests/test-runner.mjs | cat -n
fi

# Find where toolName is passed into Client.request / MCP tool call
echo "=== search for toolName usage in requests ==="
rg -n "toolName" tests/test-runner.mjs

echo "=== search for client.request / callTool / tools.get ==="
rg -n "callTool|tools\\.get|client\\.request|request\\(" tests/test-runner.mjs

# Also inspect dynamic handler JSONs for expected tool/action wiring
echo "=== dynamic handler json files ==="
ls -ლა tests/mcp-tools/dynamic-handlers || true
for f in tests/mcp-tools/dynamic-handlers/toggle-fps.json tests/mcp-tools/dynamic-handlers/clear-logs.json; do
  if [ -f "$f" ]; then
    echo "----- $f (head) -----"
    cat -n "$f" | head -n 200
  fi
done

# Inspect dynamic handler registry files for toggle_fps/clear_logs mapping
echo "=== look for dynamic-handler registry implementations ==="
ls -la src/tools/orchestration | rg "dynamic-handler" || true
rg -n "dynamic-handler|dynamic handler" src/tools/orchestration || true

# Search for tool identifiers in src for action/state names used by dynamic handler
rg -n "toggle_fps|clear_logs|toggle-fps|clear-logs" src || true

Repository: ChiR24/Unreal_mcp

Length of output: 9049


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect dynamic handler registry and dispatcher/registration code
echo "=== dynamic-handler-registry.ts ==="
cat -n src/tools/orchestration/dynamic-handler-registry.ts | head -n 260

echo "=== consolidated-handler-dispatcher.ts ==="
cat -n src/tools/orchestration/consolidated-handler-dispatcher.ts | head -n 260

echo "=== consolidated-handler-registration.ts (search for dynamic usage) ==="
rg -n "dynamic-handler-registry|toolRegistry\\.|register\\(|dispatch" src/tools/orchestration/consolidated-handler-registration.ts | head -n 200
cat -n src/tools/orchestration/consolidated-handler-registration.ts | tail -n 220

# Search for any dynamic registration/loading based on config/json
echo "=== search for dynamic-handler json loading ==="
rg -n "dynamic-handlers|mcp-tools/dynamic-handlers|toggle-fps|clear-logs|Toggle FPS|Clear Logs|dynamic handler" . || true

echo "=== search for tool names in src for completeness ==="
rg -n "toggle_fps|clear_logs|toggle-fps|clear-logs" src || true

Repository: ChiR24/Unreal_mcp

Length of output: 22452


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== list dynamic handler test jsons ==="
ls -ლა tests/mcp-tools/dynamic-handlers || true

echo "=== show clear-logs.json ==="
if [ -f tests/mcp-tools/dynamic-handlers/clear-logs.json ]; then
  cat -n tests/mcp-tools/dynamic-handlers/clear-logs.json | head -n 120
fi

echo "=== search for mcp-tools loading in tests/test-runner.mjs ==="
rg -n "mcp-tools|dynamic-handlers|dynamic-handlers/|mcp-tools/dynamic" tests/test-runner.mjs || true

echo "=== inspect runToolTests implementation range ==="
sed -n '880,1100p' tests/test-runner.mjs | cat -n

echo "=== inspect where it loads test cases / tool definitions ==="
rg -n "testCases|toolName|tool.*definition|readFile|load.*json|dynamic-handlers" tests/test-runner.mjs | head -n 80

# Focus on later portion where callToolOnce is performed using testCase.toolName
echo "=== inspect dispatch loop for toolName ==="
sed -n '1380,1605p' tests/test-runner.mjs | cat -n

Repository: ChiR24/Unreal_mcp

Length of output: 27029


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== list tests/mcp-tools/dynamic-handlers (safe) ==="
ls -la tests/mcp-tools/dynamic-handlers

echo "=== show all *.json in that folder ==="
for f in tests/mcp-tools/dynamic-handlers/*.json; do
  echo "----- $f -----"
  cat -n "$f" | head -n 120
done

echo "=== search repo for clear-logs variants ==="
rg -n "clear-logs|clear_logs|clearLogs|Clear Logs" . || true

echo "=== search repo for toggle-fps variants beyond run_dynamic_test ==="
rg -n "toggle-fps|toggle_fps|Toggle FPS|Dynamic Handlers - Toggle FPS" . || true

echo "=== search src for any dynamic tool loading/registration based on tests/mcp-tools ==="
rg -n "mcp-tools/dynamic-handlers|dynamic-handlers|dynamic-handlers/|readFile.*dynamic|load.*dynamic|register\\(.*toggle_fps|register\\(.*clear_logs" src || true

echo "=== inspect any test file about consolidated tool handlers/dynamic handlers ==="
ls -la src/tools/orchestration | head
rg -n "dynamic-handler|DynamicHandler|toolRegistry|UNKNOWN_TOOL" src/tools/orchestration || true

# Show contents of consolidated-tool-handlers.test.ts (small)
if [ -f src/tools/orchestration/consolidated-tool-handlers.test.ts ]; then
  echo "=== consolidated-tool-handlers.test.ts ==="
  cat -n src/tools/orchestration/consolidated-tool-handlers.test.ts | head -n 200
fi

Repository: ChiR24/Unreal_mcp

Length of output: 16101


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Count array entries in allToolDefinitions
python3 - <<'PY'
import re, pathlib
p = pathlib.Path("src/tools/definitions/shared/all-tool-definitions.ts")
text = p.read_text(encoding="utf-8")
m = re.search(r"export const allToolDefinitions: ToolDefinition\[\] = \[(.*?)\];", text, re.S)
if not m:
    raise SystemExit("Could not find allToolDefinitions array")
body = m.group(1)
# Count lines that look like tool definition entries (e.g. manageToolsToolDefinition,)
entries = [ln for ln in body.splitlines() if re.search(r"\b[A-Za-z0-9_]+ToolDefinition\s*,?\s*$", ln.strip())]
# Sometimes the last entry may not have trailing comma; above regex allows optional comma.
print("entry_count:", len(entries))
for e in entries:
    print(" -", e.strip().rstrip(","))
PY

Repository: ChiR24/Unreal_mcp

Length of output: 855


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where allToolDefinitions are used to build advertised MCP tools
rg -n "allToolDefinitions" src . || true

# Find where tool definitions are converted/registered with MCP server
rg -n "allToolDefinitions|allToolDefinition|ToolDefinition\\[\\]|tool(s)?\\s*=" src || true
rg -n "advertis|registerTool|tools\\s*:\\s*\\[|getTools|listTools|schema.*tool|tool.*schema" src . || true

# Search for building the server tool set (MCP)
rg -n "MCP|`@modelcontextprotocol/sdk`|Client|Tool.*Definition|server.*tool" src . | head -n 100 || true

Repository: ChiR24/Unreal_mcp

Length of output: 28694


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== search for dynamic-handlers json usage in code/tests ==="
rg -n "mcp-tools/dynamic-handlers|dynamic-handlers/" tests src || true

echo "=== inspect src/server/tool-registry.ts around tool listing/registration ==="
sed -n '1,260p' src/server/tool-registry.ts | cat -n
sed -n '260,520p' src/server/tool-registry.ts | cat -n
sed -n '520,780p' src/server/tool-registry.ts | cat -n

echo "=== inspect src/tools/dynamic/dynamic-tool-manager.ts (what tools can be dynamic) ==="
sed -n '1,220p' src/tools/dynamic/dynamic-tool-manager.ts | cat -n

echo "=== inspect src/tools/catalog/consolidated-tool-definitions.ts ==="
cat -n src/tools/catalog/consolidated-tool-definitions.ts

Repository: ChiR24/Unreal_mcp

Length of output: 23898


Unresolved root cause: toggle_fps and clear_logs are not registered as canonical parent tools, so the server will treat them as Unknown tool.

  • tests/test-runner.mjs dispatches MCP calls using testCase.toolName (toggle_fps / clear_logs).
  • src/server/tool-registry.ts rejects any tool name not present in dynamicToolManager.getToolState(name) with Unknown tool: ${name}.
  • dynamicToolManager is initialized from src/tools/catalog/consolidated-tool-definitions.ts, which is a copy of src/tools/definitions/shared/all-tool-definitions.ts; neither toggle_fps nor clear_logs exists in allToolDefinitions.
  • Even if definitions are added, src/tools/orchestration/consolidated-handler-registration.ts must also register toolRegistry.register('toggle_fps', ...) / toolRegistry.register('clear_logs', ...) to avoid UNKNOWN_TOOL at dispatch.

Fix: add both tools to src/tools/definitions/shared/all-tool-definitions.ts, register their handlers in src/tools/orchestration/consolidated-handler-registration.ts, and ensure the underlying TS/C++/bridge implementations exist for those dispatches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@run_dynamic_test.mjs` around lines 1 - 8, The test fails because tool names
"toggle_fps" and "clear_logs" are not registered, causing
dynamicToolManager.getToolState(name) to return missing and dispatch to error;
add canonical definitions for both tools into all-tool-definitions.ts (the
consolidated tool definitions) and register their handlers with
toolRegistry.register('toggle_fps', ...) and toolRegistry.register('clear_logs',
...) in consolidated-handler-registration.ts, and ensure the underlying handler
implementations (TS/C++/bridge) exist and match the toolName used by
tests/test-runner.mjs so dynamic lookups and MCP dispatch succeed.

Source: Learnings

...MATERIAL_AUTHORING_ACTIONS, ...TEXTURE_ACTIONS],
description: 'Action to perform'
...MATERIAL_AUTHORING_ACTIONS, ...TEXTURE_ACTIONS,
'replace', 'replace_references',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify whether replace and replace_references are dispatched in C++ handlers

rg -nP --type=cpp '\b(replace|replace_references)\b' -g '*AssetWorkflow*.cpp'

Repository: ChiR24/Unreal_mcp

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Locate the relevant C++ handlers file(s)
echo "== C++ AssetWorkflow handlers candidates =="
fd -t f -g '*.cpp' -e '.h' -e '.hpp' 'AssetWorkflowHandlers' . || true
fd -t f -g '*.cpp' 'McpAutomationBridge_AssetWorkflowHandlers.cpp' . || true
fd -t f -g '*.cpp' '*AssetWorkflow*' . | head -n 50

# 2) Identify the actual handler file path(s) to inspect
FILE="$(fd -t f -g 'McpAutomationBridge_AssetWorkflowHandlers.cpp' . | head -n 1 || true)"
if [ -z "${FILE}" ]; then
  echo "Could not find McpAutomationBridge_AssetWorkflowHandlers.cpp by exact name."
  # Fallback: pick first AssetWorkflow* handlers cpp
  FILE="$(fd -t f -g '*AssetWorkflow*Handlers*.cpp' . | head -n 1 || true)"
fi

if [ -z "${FILE}" ]; then
  echo "Could not find any AssetWorkflow handlers .cpp to inspect."
  exit 0
fi

echo "== Inspecting: $FILE =="
echo "-- grep for known dispatched action tokens (navigate_to_path etc.)"
rg -nP '\bnavigate_to_path\b|\bsync_to_asset\b|\bcreate_collection\b|\badd_to_collection\b|\bset_asset_color\b|\bshow_in_explorer\b|\brun_asset_action_utility\b' -- "$FILE" || true

echo "-- grep for replace tokens"
rg -nP '\breplace_references\b|\breplace\b' -- "$FILE" || true

echo "-- show nearby dispatch/switch context around any 'action' based dispatch"
rg -nP '\b(switch|if)\b.*\b(action|Action|workflow|Workflow)\b' -- "$FILE" || true

# 3) Also search the whole repo for replace tokens to see if they're dispatched under other naming (enum, prefix, etc.)
echo "== Repo-wide search for replace tokens in C++"
rg -nP '\breplace_references\b|\breplace\b' --type=cpp --glob '*AssetWorkflow*' . || true

Repository: ChiR24/Unreal_mcp

Length of output: 50614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SCOPE="plugins/McpAutomationBridge/Source/McpAutomationBridge/Private"

echo "== Locate AssetWorkflow* related C++ files =="
fd -t f -i '*assetworkflow*' "$SCOPE" || true
echo

echo "== Find where known AssetWorkflow actions are dispatched (navigate_to_path etc.) =="
rg -nP --type=cpp 'navigate_to_path|sync_to_asset|create_collection|add_to_collection|set_asset_color|show_in_explorer|run_asset_action_utility' "$SCOPE" --max-count 50 || true
echo

echo "== Search for replace / replace_references in the same C++ scope =="
rg -nP --type=cpp '\breplace_references\b|\breplace\b' "$SCOPE" --max-count 50 || true

Repository: ChiR24/Unreal_mcp

Length of output: 3248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowHandlers.cpp"

echo "== Targeted replace token checks in: $FILE =="

rg -nP 'replace_references' --type=cpp -- "$FILE" || true
rg -nP 'TEXT\("replace(_references)?"\)' --type=cpp -- "$FILE" || true
rg -nP 'Lower\s*==\s*TEXT\("replace' --type=cpp -- "$FILE" || true
rg -nP '\breplace\b' --type=cpp -- "$FILE" || true

Repository: ChiR24/Unreal_mcp

Length of output: 232


Missing C++ dispatch for replace and replace_references actions.
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/Domains/AssetWorkflow/McpAutomationBridge_AssetWorkflowHandlers.cpp dispatches other AssetWorkflow actions via if (Lower == TEXT("...")), but has no branches for TEXT("replace") or TEXT("replace_references"). Since src/tools/definitions/core/manage-asset-tool.ts advertises these actions, calls will go unhandled and can time out—add the C++ (and TS handler/test) implementations or remove these actions from the schema until supported end-to-end.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/definitions/core/manage-asset-tool.ts` at line 20, The schema
advertises "replace" and "replace_references" actions but the C++ dispatcher in
McpAutomationBridge_AssetWorkflowHandlers.cpp (the code that compares Lower ==
TEXT("...")) has no branches for TEXT("replace") or TEXT("replace_references"),
so requests will be unhandled; add corresponding dispatch branches in
McpAutomationBridge_AssetWorkflowHandlers.cpp that route those Lower values to
the existing handler functions (or new handler functions) and implement the
matching logic, and then add the TypeScript handler and tests in
manage-asset-tool.ts to exercise those actions — alternatively, if these actions
are not yet supported end-to-end, remove 'replace' and 'replace_references' from
the schema in manage-asset-tool.ts until full C++ + TS support is implemented.

Source: Learnings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant