Skip to content

feat: Movie Render Queue (MRQ) handler — 11 actions#324

Open
REMvisual wants to merge 5 commits intoChiR24:mainfrom
REMvisual:feat/mrq-handler
Open

feat: Movie Render Queue (MRQ) handler — 11 actions#324
REMvisual wants to merge 5 commits intoChiR24:mainfrom
REMvisual:feat/mrq-handler

Conversation

@REMvisual
Copy link
Copy Markdown

@REMvisual REMvisual commented Mar 30, 2026

Summary

Adds a new manage_mrq tool for full Movie Render Queue automation:

  • 11 actions: get_queue, get_job_config, get_cvars, set_cvars, get_output_settings, set_output_settings, create_job, delete_job, render, get_render_status, load_preset
  • CVar overrides: read, add/update, and remove console variable overrides per job
  • Output settings: read and modify resolution, output directory, filename format, frame overrides
  • Render control: create/delete jobs, trigger renders, check render status
  • Preset loading: load saved MRQ preset configs by asset path
  • Start/End console commands: set console commands to run before/after render

Design decisions

  • Uses legacy MovieRenderPipeline API (UMoviePipelinePrimaryConfig) for compatibility across UE 5.0–5.7. Movie Render Graph (UMovieGraphConfig) is newer but many node subclasses lack API export macros for external modules.
  • MRQ modules are optional dynamic dependencies via AddOptionalDynamicModule() with delay-load DLLs — plugin compiles and loads even if the MRQ plugin is disabled. C++ handler returns a clean error if MRQ classes aren't available.
  • Follows existing handler pattern: C++ handler in _MrqHandlers.cpp, TypeScript handler in handlers/mrq-handlers.ts, registration in consolidated files.

Files changed

File Change
McpAutomationBridge.Build.cs Added 4 optional MRQ module dependencies
McpAutomationBridgeSubsystem.h Added HandleMrqAction declaration
McpAutomationBridgeSubsystem.cpp Registered manage_mrq handler
McpAutomationBridge_MrqHandlers.cpp New — 706 lines, 11 action handlers
consolidated-tool-definitions.ts Added manage_mrq tool definition
consolidated-tool-handlers.ts Added import + registration
handlers/mrq-handlers.ts New — TypeScript MRQ handler

Test plan

Tested end-to-end on UE 5.7 (compiled clean on both 5.6 and 5.7):

  • get_queue — returns empty queue, correct job count
  • create_job — creates named job at correct index
  • get_job_config — full config dump with output settings and settings list
  • get_cvars — reads empty CVars, then confirms set CVars
  • set_cvars (set) — added r.ScreenPercentage=200, r.Shadow.MaxResolution=4096
  • set_cvars (remove) — removed single CVar, confirmed count decreased
  • get_output_settings — returns default 1920x1080
  • set_output_settings — changed to 3840x2160, custom output directory
  • load_preset — correct PRESET_NOT_FOUND error on missing asset
  • delete_job — removed job, confirmed 0 remaining
  • get_render_status — returns isRendering: false when idle

Plugin loads cleanly with MRQ plugin both enabled and disabled (optional dependency).


Open with Devin

Adds full MRQ automation support via a new manage_mrq tool with 11 actions:
get_queue, get_job_config, get_cvars, set_cvars, get_output_settings,
set_output_settings, create_job, delete_job, render, get_render_status,
load_preset.

Supports reading/writing CVar overrides, output resolution and directory,
start/end console commands, preset loading, and render execution.
Uses legacy MovieRenderPipeline API (not Movie Render Graph) for
compatibility across UE 5.0-5.7. MRQ modules are optional dynamic
dependencies — plugin loads cleanly even if MRQ plugin is disabled.

Tested end-to-end on UE 5.7 with all read/write/create/delete actions
confirmed working.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@github-actions
Copy link
Copy Markdown
Contributor

👋 Thanks for your first Pull Request! We love contributions. Please ensure you have signed off your commits and followed the contribution guidelines.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Movie Render Queue (MRQ) automation: inspect queues/jobs, create/delete jobs, load presets, start renders, and query render status via a new manage_mrq tool.
  • Bug Fixes / Validation
    • Safer inputs: path normalization with invalid-path rejection; console command validation and filtering to block unsafe entries.
  • Chores
    • Editor build now detects and enables optional MRQ integration when available.

Walkthrough

Adds Movie Render Queue (MRQ) automation: optional MRQ build modules, a new "manage_mrq" automation handler and implementation in the McpAutomationBridge subsystem, plus TypeScript tool definitions and handlers to invoke MRQ actions remotely.

Changes

Cohort / File(s) Summary
Build Configuration
plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs
Added optional dynamic modules for MRQ (MovieRenderPipelineCore, MovieRenderPipelineSettings, MovieRenderPipelineEditor, MovieRenderPipelineRenderPasses) and Win64 delay-load entries when present.
Handler Registration & Declaration
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp, plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h
Registered "manage_mrq" automation action and declared a private HandleMrqAction(...) handler on the subsystem.
MRQ Handler Implementation
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp
New HandleMrqAction(...) implementing MRQ sub-actions (queue/job inspect & mutate, CVar get/set, output settings get/set, preset loading, render control/status), with availability checks, input validation, structured JSON responses, and editor-subsystem integration.
Tool Definitions
src/tools/consolidated-tool-definitions.ts
Added manage_mrq tool definition with input/output schemas covering MRQ actions and parameters (action enum, job fields, CVars, output settings, presetPath, etc.).
Tool Handler Registration
src/tools/consolidated-tool-handlers.ts
Registered manage_mrq handler and routed requests to the MRQ tools handler.
Tool Handler Implementation
src/tools/handlers/mrq-handlers.ts
Added handleMrqTools(...) to normalize actions/paths, validate start/end command arrays, construct automation payloads, forward via executeAutomationRequest('manage_mrq', ...), and sanitize responses.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Tools
    participant Bridge as MCP_Bridge
    participant Subsystem as AutomationSubsystem
    participant MRQ as MovieRenderQueue

    Client->>Tools: manage_mrq request (action, payload)
    Tools->>Bridge: executeAutomationRequest('manage_mrq', payload)
    Bridge->>Subsystem: HandleMrqAction(RequestId, Action, Payload, Socket)
    Subsystem->>Subsystem: validate & normalize action/payload
    alt MRQ modules available
        Subsystem->>MRQ: acquire Queue subsystem / job operations
        MRQ-->>Subsystem: job/queue data or status
        Subsystem-->>Bridge: Send success response (result JSON)
    else MRQ modules unavailable
        Subsystem-->>Bridge: Send MRQ_NOT_AVAILABLE error
    end
    Bridge-->>Tools: Relay response
    Tools-->>Client: Return final response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hopped through queues where renders grow,
Jobs like carrots in neat little rows,
Presets snug, CVars hum in tune,
I nudged the button—frames bloom by the moon,
Hooray, pixels sparkle — nibble the proof! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main addition: a Movie Render Queue handler with 11 actions, which matches the primary purpose of the changeset.
Description check ✅ Passed The PR description covers all template sections: Summary (with tool overview), Changes (detailed file-by-file), Related Issues (none mentioned), Type of Change (✨ New feature unchecked but evident), Testing (end-to-end validation on UE 5.7 reported), and Pre-Merge Checklist partially addressed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added enhancement New feature or request size/l area/tools labels Mar 30, 2026
devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

…lidation

Fixes all 8 review findings from Devin AI and CodeRabbit:

C++ (McpAutomationBridge_MrqHandlers.cpp):
- Add null checks after Cast for CVars and Output pointers in set_cvars
  and set_output_settings handlers (prevents nullptr dereference)
- Sanitize outputDirectory: normalize path, reject .. traversal
- Validate create_job: require map and sequence before allocating job
- Populate ErrorCode (6th param) on all SendAutomationResponse failure
  paths so top-level error field is never empty

TypeScript (mrq-handlers.ts):
- Normalize asset paths (map, sequence, presetPath) to canonical
  /Game/... form before forwarding to C++ bridge
- Validate startCommands/endCommands through CommandValidator safety
  filter to prevent dangerous console command injection
- Use requireAction() for consistent action extraction

Schema (consolidated-tool-definitions.ts):
- Add missing set_output_settings input fields (zeroPadFrameNumbers,
  frameNumberOffset, handleFrameCount, useCustomFrameRate,
  useCustomPlaybackRange, customStartFrame, customEndFrame)
- Add outputSchema defining full MRQ response shapes

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@coderabbitai coderabbitai bot added javascript Pull requests that update javascript code area/tools labels Mar 31, 2026
coderabbitai[bot]

This comment was marked as resolved.

Addresses CodeRabbit's second round of review:

- set_cvars: early return if Payload is null (prevents nullptr dereference
  when accessing Payload->TryGetObjectField later)
- set_output_settings: same null Payload guard
- delete_job: require explicit jobIndex field — destructive action should
  not silently default to job 0 on malformed requests

Read-only handlers (get_cvars, get_output_settings, etc.) keep the
default-to-0 behavior, which is intentional and documented in the schema.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp (2)

339-340: ⚠️ Potential issue | 🟠 Major

Require jobIndex on the remaining mutating actions.

set_cvars, set_output_settings, and load_preset still fall back to 0 when the field is omitted, so a malformed request silently rewrites job 0 instead of failing fast. delete_job already uses the safer pattern just below.

🛠 Suggested pattern
-        int32 JobIndex = 0;
-        Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex);
+        int32 JobIndex = INDEX_NONE;
+        if (!Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex))
+        {
+            TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
+            Err->SetStringField(TEXT("error"), TEXT("MISSING_JOB_INDEX"));
+            SendAutomationResponse(RequestingSocket, RequestId, false,
+                TEXT("set_cvars requires explicit jobIndex"), Err, TEXT("MISSING_JOB_INDEX"));
+            return true;
+        }

Apply the same check in set_output_settings and load_preset.

Also applies to: 495-496, 709-715

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 339 - 340, The mutating handlers set_cvars, set_output_settings,
and load_preset currently call Payload->TryGetNumberField(TEXT("jobIndex"),
JobIndex) and silently default JobIndex to 0; update each handler to require the
jobIndex field the same way delete_job does: declare int32 JobIndex; check
Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex) and if it returns false,
respond with an error/early return (matching the existing delete_job error path)
instead of proceeding, so malformed requests cannot mutate job 0; apply this
change for the occurrences around the JobIndex uses (including the instances
mentioned near lines 495-496 and 709-715).

532-547: ⚠️ Potential issue | 🔴 Critical

outputDirectory still needs the project-path sanitizer.

The current check only rejects visible .. segments; it still accepts absolute destinations and other out-of-project paths. Please run the input through SanitizeProjectRelativePath() before persisting it.

🔒 Suggested fix
         if (Payload->TryGetStringField(TEXT("outputDirectory"), StrVal))
         {
-            // Normalize and reject path traversal
-            FString CleanDir = StrVal;
-            FPaths::NormalizeFilename(CleanDir);
-            FPaths::CollapseRelativeDirectories(CleanDir);
-            if (CleanDir.Contains(TEXT("..")))
+            FString SanitizedDir;
+            if (!SanitizeProjectRelativePath(StrVal, SanitizedDir))
             {
                 TSharedPtr<FJsonObject> Err2 = MakeShared<FJsonObject>();
                 Err2->SetStringField(TEXT("error"), TEXT("INVALID_PATH"));
                 SendAutomationResponse(RequestingSocket, RequestId, false,
-                    TEXT("outputDirectory rejected: path traversal (..) not allowed"), Err2, TEXT("INVALID_PATH"));
+                    TEXT("outputDirectory must stay within the project"), Err2, TEXT("INVALID_PATH"));
                 return true;
             }
-            Output->OutputDirectory.Path = CleanDir;
+            Output->OutputDirectory.Path = SanitizedDir;
         }
As per coding guidelines, "Use `SanitizeProjectRelativePath()` to validate and sanitize all file paths to prevent directory traversal attacks" and "Do not use absolute Windows paths in handler code - use relative paths or UE's path resolution utilities".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 532 - 547, The outputDirectory value extracted via
Payload->TryGetStringField(TEXT("outputDirectory"), StrVal) must be validated
with SanitizeProjectRelativePath() before assigning to
Output->OutputDirectory.Path; replace the current manual
NormalizeFilename/CollapseRelativeDirectories/.. check with a call to
SanitizeProjectRelativePath(StrVal, /*out*/ CleanDir) (or the project's exact
sanitizer signature), check its return (or whether CleanDir is empty/invalid)
and send the same INVALID_PATH response via
SendAutomationResponse(RequestingSocket, RequestId, ...) if sanitization fails,
otherwise assign the sanitized CleanDir to Output->OutputDirectory.Path.
🧹 Nitpick comments (1)
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp (1)

103-107: Please move these payload branches onto typed USTRUCTs.

This new handler is hand-parsing every action payload, which makes defaults and required-field rules easy to drift between branches. Defining per-action request/response structs and marshalling them via FJsonObjectConverter would make the contract much easier to keep consistent.

As per coding guidelines, "Use FJsonObjectConverter for JSON parsing and struct serialization instead of manual JSON parsing".

Also applies to: 176-177, 274-275, 339-340, 440-441, 495-496, 593-597, 711-715

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 103 - 107, Replace the ad-hoc JSON field extraction (e.g., the
local FString SubAction and Payload->TryGetStringField usage) with typed USTRUCT
request/response types for each action and use FJsonObjectConverter to marshal
Payload into those structs; create per-action USTRUCTs (e.g., FMyActionRequest /
FMyActionResponse), update the handler functions in
McpAutomationBridge_MrqHandlers.cpp to call
FJsonObjectConverter::JsonObjectToUStruct on Payload and validate
required/defaulted fields on the struct rather than hand-parsing, and then use
the typed fields in the existing logic (replace occurrences of
Payload->TryGetStringField, Payload.IsValid() branches and similar branches at
the other noted locations with struct-driven handling).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`:
- Around line 339-340: The mutating handlers set_cvars, set_output_settings, and
load_preset currently call Payload->TryGetNumberField(TEXT("jobIndex"),
JobIndex) and silently default JobIndex to 0; update each handler to require the
jobIndex field the same way delete_job does: declare int32 JobIndex; check
Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex) and if it returns false,
respond with an error/early return (matching the existing delete_job error path)
instead of proceeding, so malformed requests cannot mutate job 0; apply this
change for the occurrences around the JobIndex uses (including the instances
mentioned near lines 495-496 and 709-715).
- Around line 532-547: The outputDirectory value extracted via
Payload->TryGetStringField(TEXT("outputDirectory"), StrVal) must be validated
with SanitizeProjectRelativePath() before assigning to
Output->OutputDirectory.Path; replace the current manual
NormalizeFilename/CollapseRelativeDirectories/.. check with a call to
SanitizeProjectRelativePath(StrVal, /*out*/ CleanDir) (or the project's exact
sanitizer signature), check its return (or whether CleanDir is empty/invalid)
and send the same INVALID_PATH response via
SendAutomationResponse(RequestingSocket, RequestId, ...) if sanitization fails,
otherwise assign the sanitized CleanDir to Output->OutputDirectory.Path.

---

Nitpick comments:
In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`:
- Around line 103-107: Replace the ad-hoc JSON field extraction (e.g., the local
FString SubAction and Payload->TryGetStringField usage) with typed USTRUCT
request/response types for each action and use FJsonObjectConverter to marshal
Payload into those structs; create per-action USTRUCTs (e.g., FMyActionRequest /
FMyActionResponse), update the handler functions in
McpAutomationBridge_MrqHandlers.cpp to call
FJsonObjectConverter::JsonObjectToUStruct on Payload and validate
required/defaulted fields on the struct rather than hand-parsing, and then use
the typed fields in the existing logic (replace occurrences of
Payload->TryGetStringField, Payload.IsValid() branches and similar branches at
the other noted locations with struct-driven handling).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 68acc211-aa4b-42af-951f-4c1338eea0fd

📥 Commits

Reviewing files that changed from the base of the PR and between d941fec and 27cf089.

📒 Files selected for processing (1)
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp

@ChiR24
Copy link
Copy Markdown
Owner

ChiR24 commented Apr 1, 2026

Thanks for the MRQ implementation! I reviewed the full diff and have one critical issue and a few minor ones.

Critical: UE 5.0-5.1 Compilation Failure

The file header (lines 30-32) claims:
\\cpp
// UE 5.0-5.1: UMoviePipelineMasterConfig
// UE 5.2+: UMoviePipelinePrimaryConfig
// Both are handled via __has_include checks.
\\

But this is not implemented. There's only one __has_include\ check:

\\cpp
#if __has_include(\MoviePipelineQueueSubsystem.h)
#define MCP_HAS_MRQ 1
#include \MoviePipelinePrimaryConfig.h\ // ← Does NOT exist in UE 5.0-5.1
\\

Verified across all UE versions:

UE Version \MoviePipelineMasterConfig.h\ \MoviePipelinePrimaryConfig.h\
5.0-5.1 ✅ EXISTS MISSING
5.2-5.4 ✅ (deprecated typedef) ✅ EXISTS
5.5-5.7 ❌ REMOVED ✅ EXISTS

The code also uses \UMoviePipelinePrimaryConfig\ directly in ~12 places (Casts, LoadObject), which will fail to compile on UE 5.0-5.1 even if the include is fixed.

Suggested fix:
\\cpp
#if __has_include(\MoviePipelinePrimaryConfig.h)
#include \MoviePipelinePrimaryConfig.h
using MRQConfigClass = UMoviePipelinePrimaryConfig;
#elif __has_include(\MoviePipelineMasterConfig.h)
#include \MoviePipelineMasterConfig.h
using MRQConfigClass = UMoviePipelineMasterConfig;
#endif
\\

Then replace all \UMoviePipelinePrimaryConfig*\ with \MRQConfigClass*\ throughout.


Minor Issues

  1. \set_cvars\ and \set_output_settings\ silently default \jobIndex\ to 0 if the field is missing. Could silently mutate the wrong job. Consider requiring explicit \jobIndex\ like \delete_job\ does.

  2. \outputDirectory\ validation only rejects ..\ but allows absolute paths (\C:\) and UNC paths (\\\server\). Consider using \SanitizeProjectRelativePath()\ for defense-in-depth.


What Looks Good

  • 11 actions covering full MRQ workflow
  • TypeScript handler validates console commands through \CommandValidator\
  • Path normalization in \create_job\ / \load_preset\
  • Proper error codes (\INVALID_JOB_INDEX, \NO_QUEUE, \ALREADY_RENDERING, etc.)
  • Optional module pattern in Build.cs
  • Category \�uthoring\ fits the cinematic/rendering workflow

Once the UE 5.0-5.1 compatibility is fixed, this is ready to merge.

UE 5.0-5.1 used MoviePipelineMasterConfig.h (UMoviePipelineMasterConfig),
renamed to MoviePipelinePrimaryConfig.h in 5.2+. Add nested __has_include
check with a using alias so the handler compiles on all UE 5.x versions.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp (2)

182-183: ⚠️ Potential issue | 🟠 Major

Require explicit jobIndex instead of defaulting to 0.

At Lines 345, 501, and 719 (and current read branches), missing jobIndex silently targets job 0. For mutating actions, this can modify the wrong job on malformed requests.

✅ Suggested pattern (apply to all affected branches)
-        int32 JobIndex = 0;
-        Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex);
+        int32 JobIndex = INDEX_NONE;
+        if (!Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex))
+        {
+            TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
+            Err->SetStringField(TEXT("error"), TEXT("MISSING_JOB_INDEX"));
+            SendAutomationResponse(RequestingSocket, RequestId, false,
+                TEXT("This action requires explicit jobIndex"), Err, TEXT("MISSING_JOB_INDEX"));
+            return true;
+        }

Also applies to: 280-281, 345-347, 446-447, 501-503, 715-721

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 182 - 183, Currently code defaults JobIndex to 0 when
Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex) fails; change to require
an explicit jobIndex by first declaring an uninitialized int32 (e.g., int32
JobIndex;) or a sentinel, call Payload->TryGetNumberField and check its boolean
result, and if it returns false or the value is out of range return an
error/early-fail response for the request instead of proceeding; apply this
pattern to all handlers that use JobIndex (the occurrences using JobIndex and
Payload->TryGetNumberField in McpAutomationBridge_MrqHandlers.cpp) so mutating
actions never silently target job 0.

538-553: ⚠️ Potential issue | 🔴 Critical

Harden outputDirectory validation using SanitizeProjectRelativePath().

Line 552 still persists a user path after only .. checks; absolute (C:\...) and UNC (\\...) paths can still pass.

🔒 Proposed fix
         if (Payload->TryGetStringField(TEXT("outputDirectory"), StrVal))
         {
-            // Normalize and reject path traversal
-            FString CleanDir = StrVal;
-            FPaths::NormalizeFilename(CleanDir);
-            FPaths::CollapseRelativeDirectories(CleanDir);
-            if (CleanDir.Contains(TEXT("..")))
+            FString SanitizedDir;
+            if (!SanitizeProjectRelativePath(StrVal, SanitizedDir))
             {
                 TSharedPtr<FJsonObject> Err2 = MakeShared<FJsonObject>();
                 Err2->SetStringField(TEXT("error"), TEXT("INVALID_PATH"));
                 SendAutomationResponse(RequestingSocket, RequestId, false,
-                    TEXT("outputDirectory rejected: path traversal (..) not allowed"), Err2, TEXT("INVALID_PATH"));
+                    TEXT("outputDirectory must be a valid project-relative path"), Err2, TEXT("INVALID_PATH"));
                 return true;
             }
-            Output->OutputDirectory.Path = CleanDir;
+            Output->OutputDirectory.Path = SanitizedDir;
         }
As per coding guidelines, use `SanitizeProjectRelativePath()` to validate and sanitize all file paths to prevent directory traversal attacks.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 538 - 553, The current validation for outputDirectory (inside the
Payload->TryGetStringField block) only checks for ".." and can still accept
absolute or UNC paths; replace this with UE's SanitizeProjectRelativePath() call
to validate and normalize the path before assigning to
Output->OutputDirectory.Path. Specifically, call
SanitizeProjectRelativePath(StrVal, /*out*/ CleanDir) (or the correct overload)
and if it returns false or produces an empty result, build the Err2 JSON and
call SendAutomationResponse(RequestingSocket, RequestId, false,
TEXT("outputDirectory rejected: invalid or non-project-relative path"), Err2,
TEXT("INVALID_PATH")) and return true; otherwise assign the sanitized CleanDir
to Output->OutputDirectory.Path. Ensure you keep the same error JSON shape and
early-return behavior used currently in this function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`:
- Around line 20-23: The header block in McpAutomationBridge_MrqHandlers.cpp
incorrectly lists a non-existent action "save_preset"; update the comment to
reflect only implemented actions (e.g., keep "load_preset" and remove
"save_preset") or, if saving should be supported, add the corresponding handler
implementation instead; edit the comment near the top of the file (the Section
3: Preset Management block) to either remove "save_preset" or replace it with
the actual implemented action names so the header accurately documents the file.
- Around line 109-113: The code currently parses JSON payloads field-by-field
(e.g., the Payload variable and SubAction extraction in
McpAutomationBridge_MrqHandlers.cpp) which should be replaced by structured
deserialization using FJsonObjectConverter; define USTRUCTs (or plain UStructs)
matching each action's payload (including jobIndex with the correct type and
optional/default handling), then replace repeated Payload->TryGet*Field calls
across the handlers (the blocks around lines 109–113, 335–440, 490–583, etc.)
with FJsonObjectConverter::JsonObjectToUStruct or JsonObjectStringToUStruct
calls to populate those structs, validate the resulting struct fields, and
update the handler functions to use the strongly typed members instead of manual
TryGet*Field parsing.

---

Duplicate comments:
In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`:
- Around line 182-183: Currently code defaults JobIndex to 0 when
Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex) fails; change to require
an explicit jobIndex by first declaring an uninitialized int32 (e.g., int32
JobIndex;) or a sentinel, call Payload->TryGetNumberField and check its boolean
result, and if it returns false or the value is out of range return an
error/early-fail response for the request instead of proceeding; apply this
pattern to all handlers that use JobIndex (the occurrences using JobIndex and
Payload->TryGetNumberField in McpAutomationBridge_MrqHandlers.cpp) so mutating
actions never silently target job 0.
- Around line 538-553: The current validation for outputDirectory (inside the
Payload->TryGetStringField block) only checks for ".." and can still accept
absolute or UNC paths; replace this with UE's SanitizeProjectRelativePath() call
to validate and normalize the path before assigning to
Output->OutputDirectory.Path. Specifically, call
SanitizeProjectRelativePath(StrVal, /*out*/ CleanDir) (or the correct overload)
and if it returns false or produces an empty result, build the Err2 JSON and
call SendAutomationResponse(RequestingSocket, RequestId, false,
TEXT("outputDirectory rejected: invalid or non-project-relative path"), Err2,
TEXT("INVALID_PATH")) and return true; otherwise assign the sanitized CleanDir
to Output->OutputDirectory.Path. Ensure you keep the same error JSON shape and
early-return behavior used currently in this function.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6073a54-5a8e-478b-ace5-c3da013cdf8e

📥 Commits

Reviewing files that changed from the base of the PR and between 27cf089 and 6687cea.

📒 Files selected for processing (1)
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp

Comment on lines +109 to +113
FString SubAction;
if (Payload.IsValid())
{
Payload->TryGetStringField(TEXT("action"), SubAction);
}
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.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify manual JSON parsing density and absence of FJsonObjectConverter usage in this handler.
rg -n 'TryGet(String|Number|Bool|Object|Array)Field|Set(String|Number|Bool|Object|Array)Field' plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp
rg -n 'FJsonObjectConverter' plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp

Repository: ChiR24/Unreal_mcp

Length of output: 10061


Refactor to use FJsonObjectConverter for payload deserialization.

This file contains extensive manual JSON field parsing throughout (lines 109–113, 335–440, 490–583, and beyond). Per coding guidelines, structured payloads should be deserialized using FJsonObjectConverter instead of repeated field-by-field TryGet*Field calls. This approach will eliminate parsing drift, enforce consistent validation (e.g., jobIndex handling), and reduce code duplication across action handlers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 109 - 113, The code currently parses JSON payloads field-by-field
(e.g., the Payload variable and SubAction extraction in
McpAutomationBridge_MrqHandlers.cpp) which should be replaced by structured
deserialization using FJsonObjectConverter; define USTRUCTs (or plain UStructs)
matching each action's payload (including jobIndex with the correct type and
optional/default handling), then replace repeated Payload->TryGet*Field calls
across the handlers (the blocks around lines 109–113, 335–440, 490–583, etc.)
with FJsonObjectConverter::JsonObjectToUStruct or JsonObjectStringToUStruct
calls to populate those structs, validate the resulting struct fields, and
update the handler functions to use the strongly typed members instead of manual
TryGet*Field parsing.

The header listed save_preset as an implemented action but it was never
added. Remove the misleading comment.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp (3)

344-346: ⚠️ Potential issue | 🟠 Major

Require explicit jobIndex in all mutating actions.

set_cvars, set_output_settings, and load_preset still proceed with implicit jobIndex = 0 when the field is missing, which can silently mutate the wrong job.

Suggested fix
- int32 JobIndex = 0;
- Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex);
+ int32 JobIndex = 0;
+ if (!Payload->TryGetNumberField(TEXT("jobIndex"), JobIndex))
+ {
+     TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
+     Err->SetStringField(TEXT("error"), TEXT("MISSING_JOB_INDEX"));
+     SendAutomationResponse(RequestingSocket, RequestId, false,
+         TEXT("This action requires explicit jobIndex"), Err, TEXT("MISSING_JOB_INDEX"));
+     return true;
+ }

Also applies to: 500-501, 714-720

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 344 - 346, The handlers currently default JobIndex to 0 when
calling Payload->TryGetNumberField, which lets mutating actions silently affect
job 0; update each mutating handler (e.g., the set_cvars, set_output_settings,
load_preset handlers in McpAutomationBridge_MrqHandlers.cpp and the other
occurrences around the indicated ranges) to require the "jobIndex" field: remove
implicit initialization to 0, call Payload->TryGetNumberField(TEXT("jobIndex"),
JobIndex) and check its boolean return (or validate presence) and if missing
return an error/early-fail (logging or sending a failure response) instead of
proceeding; ensure you reference the JobIndex variable and the exact handler
function names when adding the presence check so no handler proceeds without an
explicit jobIndex.

537-552: ⚠️ Potential issue | 🔴 Critical

Harden outputDirectory validation with SanitizeProjectRelativePath().

Current checks block .. but still permit absolute/UNC paths. This should use the project sanitizer before persisting.

Suggested fix
 if (Payload->TryGetStringField(TEXT("outputDirectory"), StrVal))
 {
-    // Normalize and reject path traversal
-    FString CleanDir = StrVal;
-    FPaths::NormalizeFilename(CleanDir);
-    FPaths::CollapseRelativeDirectories(CleanDir);
-    if (CleanDir.Contains(TEXT("..")))
+    FString SanitizedDir;
+    if (!SanitizeProjectRelativePath(StrVal, SanitizedDir))
     {
         TSharedPtr<FJsonObject> Err2 = MakeShared<FJsonObject>();
         Err2->SetStringField(TEXT("error"), TEXT("INVALID_PATH"));
         SendAutomationResponse(RequestingSocket, RequestId, false,
-            TEXT("outputDirectory rejected: path traversal (..) not allowed"), Err2, TEXT("INVALID_PATH"));
+            TEXT("outputDirectory must be a valid project-relative path"), Err2, TEXT("INVALID_PATH"));
         return true;
     }
-    Output->OutputDirectory.Path = CleanDir;
+    Output->OutputDirectory.Path = SanitizedDir;
 }
As per coding guidelines, use `SanitizeProjectRelativePath()` to validate and sanitize all file paths to prevent directory traversal attacks.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 537 - 552, The outputDirectory handling currently normalizes and
checks for ".." but still allows absolute/UNC paths; replace the manual checks
by passing the extracted StrVal through SanitizeProjectRelativePath() (use
Payload->TryGetStringField to obtain StrVal), verify the returned FString is
non-empty and does not start with a path root, and only then assign it to
Output->OutputDirectory.Path; on failure call
SendAutomationResponse(RequestingSocket, RequestId, false, ...) with an
INVALID_PATH error (keep existing error semantics) so paths are properly
sanitized and rejected when unsafe.

108-113: 🛠️ Refactor suggestion | 🟠 Major

Move payload parsing to typed structs via FJsonObjectConverter.

Manual TryGet*Field parsing is pervasive and drifts validation behavior across actions; this should be consolidated with structured deserialization.

As per coding guidelines, use FJsonObjectConverter for JSON parsing and struct serialization instead of manual JSON parsing.

Also applies to: 335-440, 489-583

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`
around lines 108 - 113, Replace manual field extraction (the SubAction variable
and Payload->TryGetStringField usage) with structured deserialization using
FJsonObjectConverter: define a lightweight UStruct (e.g., FMrqActionPayload with
an FString Action and other expected fields) and call
FJsonObjectConverter::JsonObjectToUStruct on the incoming Payload to populate
it, then use the typed struct's Action (ToLower) instead of SubAction; repeat
the same pattern for the other handler blocks referenced (around lines 335-440
and 489-583) so all JSON parsing uses the same typed structs and
FJsonObjectConverter for consistent validation and easier maintenance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp`:
- Around line 344-346: The handlers currently default JobIndex to 0 when calling
Payload->TryGetNumberField, which lets mutating actions silently affect job 0;
update each mutating handler (e.g., the set_cvars, set_output_settings,
load_preset handlers in McpAutomationBridge_MrqHandlers.cpp and the other
occurrences around the indicated ranges) to require the "jobIndex" field: remove
implicit initialization to 0, call Payload->TryGetNumberField(TEXT("jobIndex"),
JobIndex) and check its boolean return (or validate presence) and if missing
return an error/early-fail (logging or sending a failure response) instead of
proceeding; ensure you reference the JobIndex variable and the exact handler
function names when adding the presence check so no handler proceeds without an
explicit jobIndex.
- Around line 537-552: The outputDirectory handling currently normalizes and
checks for ".." but still allows absolute/UNC paths; replace the manual checks
by passing the extracted StrVal through SanitizeProjectRelativePath() (use
Payload->TryGetStringField to obtain StrVal), verify the returned FString is
non-empty and does not start with a path root, and only then assign it to
Output->OutputDirectory.Path; on failure call
SendAutomationResponse(RequestingSocket, RequestId, false, ...) with an
INVALID_PATH error (keep existing error semantics) so paths are properly
sanitized and rejected when unsafe.
- Around line 108-113: Replace manual field extraction (the SubAction variable
and Payload->TryGetStringField usage) with structured deserialization using
FJsonObjectConverter: define a lightweight UStruct (e.g., FMrqActionPayload with
an FString Action and other expected fields) and call
FJsonObjectConverter::JsonObjectToUStruct on the incoming Payload to populate
it, then use the typed struct's Action (ToLower) instead of SubAction; repeat
the same pattern for the other handler blocks referenced (around lines 335-440
and 489-583) so all JSON parsing uses the same typed structs and
FJsonObjectConverter for consistent validation and easier maintenance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d0f6e6bb-6c51-4f08-9a78-69e9cc9c5874

📥 Commits

Reviewing files that changed from the base of the PR and between 6687cea and 5f4fd43.

📒 Files selected for processing (1)
  • plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MrqHandlers.cpp

@ChiR24
Copy link
Copy Markdown
Owner

ChiR24 commented Apr 2, 2026

Thanks for addressing the \MoviePipelinePrimaryConfig\ header compatibility. However, there's a second critical UE 5.0-5.1 incompatibility that wasn't addressed.

The \UMoviePipelineConsoleVariableSetting\ API changed significantly between UE 5.1 and 5.2:

UE 5.0-5.1:
\\cpp
// Direct TMap access - no helper methods
TMap<FString, float> ConsoleVariables;
\\

UE 5.2+:
\\cpp
TArray CVars;
TArray GetConsoleVariables() const;
bool AddOrUpdateConsoleVariable(const FString& Name, float Value);
bool RemoveConsoleVariable(const FString& Name);
\\

The PR uses the UE 5.2+ API in \get_cvars\ and \set_cvars\ handlers:
\\cpp
CVars->GetConsoleVariables(); // Won't compile on 5.0-5.1
CVars->AddOrUpdateConsoleVariable(...); // Won't compile on 5.0-5.1
CVars->RemoveConsoleVariable(...); // Won't compile on 5.0-5.1
\\

Additionally, \FMoviePipelineConsoleVariableEntry\ doesn't exist in UE 5.0-5.1.

Fix needed: Add version-specific handling:

\\cpp
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2
// UE 5.2+ API
TArray CVarEntries = CVars->GetConsoleVariables();
CVars->AddOrUpdateConsoleVariable(Name, Value);
#else
// UE 5.0-5.1 API - direct TMap access
for (const auto& Pair : CVars->ConsoleVariables) {
// Handle as {Pair.Key, Pair.Value}
}
CVars->ConsoleVariables.Add(Name, Value);
CVars->ConsoleVariables.Remove(Name);
#endif
\\


Other Minor Issues

  1. \set_cvars\ and \set_output_settings\ default \jobIndex\ to 0 silently — could mutate wrong job
  2. \outputDirectory\ validation allows absolute paths (\C:\) and UNC paths (\\\server\)

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

Labels

area/plugin area/server enhancement New feature or request javascript Pull requests that update javascript code size/l

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants