Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ public McpAutomationBridge(ReadOnlyTargetRules Target) : base(Target)
AddOptionalDynamicModule(Target, EngineDir, "ChaosVehicles", "ChaosVehicles");
AddOptionalDynamicModule(Target, EngineDir, "AnimationData", "AnimationData");

// Movie Render Queue / Pipeline (optional plugin) - for MRQ automation
AddOptionalDynamicModule(Target, EngineDir, "MovieRenderPipelineCore", "MovieRenderPipelineCore");
AddOptionalDynamicModule(Target, EngineDir, "MovieRenderPipelineSettings", "MovieRenderPipelineSettings");
AddOptionalDynamicModule(Target, EngineDir, "MovieRenderPipelineEditor", "MovieRenderPipelineEditor");
AddOptionalDynamicModule(Target, EngineDir, "MovieRenderPipelineRenderPasses", "MovieRenderPipelineRenderPasses");

// Ensure editor builds expose full Blueprint graph editing APIs.
PublicDefinitions.Add("MCP_HAS_K2NODE_HEADERS=1");
PublicDefinitions.Add("MCP_HAS_EDGRAPH_SCHEMA_K2=1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,14 @@ void UMcpAutomationBridgeSubsystem::InitializeHandlers() {
return true;
#endif
});

// Movie Render Queue (MRQ)
RegisterHandler(TEXT("manage_mrq"),
[this](const FString &R, const FString &A,
const TSharedPtr<FJsonObject> &P,
TSharedPtr<FMcpBridgeWebSocket> S) {
return HandleMrqAction(R, A, P, S);
});
}

// Drain and process any automation requests that were enqueued while the
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,11 @@ class MCPAUTOMATIONBRIDGE_API UMcpAutomationBridgeSubsystem
const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket);

// Movie Render Queue (MRQ)
bool HandleMrqAction(const FString &RequestId, const FString &Action,
const TSharedPtr<FJsonObject> &Payload,
TSharedPtr<FMcpBridgeWebSocket> RequestingSocket);

private:
// Ticker handle for managing the subsystems tick function
FTSTicker::FDelegateHandle TickHandle;
Expand Down
82 changes: 82 additions & 0 deletions src/tools/consolidated-tool-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4699,5 +4699,87 @@ export const consolidatedToolDefinitions: ToolDefinition[] = [
error: commonSchemas.stringProp
}
}
},
{
name: 'manage_mrq',
category: 'authoring',
description: 'Movie Render Queue (MRQ) management. Read/write render queue jobs, CVar overrides, output settings, presets, and trigger renders. 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.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: [
'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'
],
description: 'MRQ action to perform'
},
jobIndex: { type: 'number', description: 'Job index in the queue (default 0)' },
map: { type: 'string', description: 'Map path for create_job (e.g., /Game/Maps/MyLevel.MyLevel)' },
sequence: { type: 'string', description: 'Level sequence path for create_job' },
jobName: { type: 'string', description: 'Job name for create_job' },
set: { type: 'object', description: 'CVar overrides to set (key: cvar name, value: float). Used with set_cvars.' },
cvars: { type: 'object', description: 'Alias for set — CVar overrides as { name: value } pairs' },
remove: { type: 'array', items: { type: 'string' }, description: 'CVar names to remove. Used with set_cvars.' },
startCommands: { type: 'array', items: { type: 'string' }, description: 'Console commands to run before render' },
endCommands: { type: 'array', items: { type: 'string' }, description: 'Console commands to run after render' },
outputDirectory: { type: 'string', description: 'Output directory path for set_output_settings' },
resolutionX: { type: 'number', description: 'Output width for set_output_settings' },
resolutionY: { type: 'number', description: 'Output height for set_output_settings' },
fileNameFormat: { type: 'string', description: 'Filename format with tokens like {sequence_name}, {frame_number}' },
overrideExisting: { type: 'boolean', description: 'Overwrite existing output files' },
zeroPadFrameNumbers: { type: 'number', description: 'Number of digits for zero-padded frame numbers (default 4)' },
frameNumberOffset: { type: 'number', description: 'Offset added to frame numbers in output filenames' },
handleFrameCount: { type: 'number', description: 'Number of extra frames to render before/after sequence range' },
useCustomFrameRate: { type: 'boolean', description: 'Use a custom frame rate for output' },
useCustomPlaybackRange: { type: 'boolean', description: 'Use custom start/end frames instead of sequence range' },
customStartFrame: { type: 'number', description: 'Custom start frame (requires useCustomPlaybackRange)' },
customEndFrame: { type: 'number', description: 'Custom end frame (requires useCustomPlaybackRange)' },
presetPath: { type: 'string', description: 'Asset path to MRQ preset config for load_preset' }
},
required: ['action']
},
outputSchema: {
type: 'object',
properties: {
success: { type: 'boolean' },
message: commonSchemas.stringProp,
error: commonSchemas.stringProp,
result: {
type: 'object',
description: 'Action-specific result object',
properties: {
jobs: { type: 'array', description: 'Array of job objects (get_queue)' },
jobCount: { type: 'number' },
isRendering: { type: 'boolean' },
jobIndex: { type: 'number' },
jobName: commonSchemas.stringProp,
consoleVariables: { type: 'object', description: 'CVar name→value pairs' },
startConsoleCommands: { type: 'array', items: { type: 'string' } },
endConsoleCommands: { type: 'array', items: { type: 'string' } },
totalCVars: { type: 'number' },
outputDirectory: commonSchemas.stringProp,
resolutionX: { type: 'number' },
resolutionY: { type: 'number' },
fileNameFormat: commonSchemas.stringProp,
overrideExisting: { type: 'boolean' },
zeroPadFrameNumbers: { type: 'number' },
frameNumberOffset: { type: 'number' },
handleFrameCount: { type: 'number' },
useCustomFrameRate: { type: 'boolean' },
useCustomPlaybackRange: { type: 'boolean' },
customStartFrame: { type: 'number' },
customEndFrame: { type: 'number' },
remainingJobs: { type: 'number' },
renderStarted: { type: 'boolean' },
presetPath: commonSchemas.stringProp,
settingCount: { type: 'number' }
}
}
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
];
6 changes: 6 additions & 0 deletions src/tools/consolidated-tool-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { handleVolumeTools } from './handlers/volume-handlers.js';
import { handleNavigationTools } from './handlers/navigation-handlers.js';
import { handleSplineTools } from './handlers/spline-handlers.js';
import { handleManageToolsTools } from './handlers/manage-tools-handlers.js';
import { handleMrqTools } from './handlers/mrq-handlers.js';
// import { getDynamicHandlerForTool } from './dynamic-handler-registry.js';
// import { consolidatedToolDefinitions } from './consolidated-tool-definitions.js';

Expand Down Expand Up @@ -463,6 +464,11 @@ function registerDefaultHandlers() {

// 40. SPLINE SYSTEM (Phase 26)
toolRegistry.register('manage_splines', async (args, tools) => await handleSplineTools(getAction(args), args, tools));

// MRQ - Movie Render Queue
toolRegistry.register('manage_mrq', async (args, tools) => {
return await handleMrqTools(getAction(args), args, tools);
});
}

// Initialize default handlers immediately
Expand Down
117 changes: 117 additions & 0 deletions src/tools/handlers/mrq-handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { cleanObject } from '../../utils/safe-json.js';
import { ITools } from '../../types/tool-interfaces.js';
import { executeAutomationRequest, requireAction } from './common-handlers.js';
import { CommandValidator } from '../../utils/command-validator.js';

/**
* Normalize asset path fields to canonical /Game/... form.
*/
function normalizePathFields(args: Record<string, unknown>, fields: string[]): Record<string, unknown> {
const result = { ...args };
for (const field of fields) {
const value = result[field];
if (typeof value === 'string' && value.length > 0) {
let normalized = value.replace(/\\/g, '/');
if (normalized.startsWith('/Content/')) {
normalized = '/Game/' + normalized.slice('/Content/'.length);
}
if (!normalized.startsWith('/')) {
normalized = '/Game/' + normalized;
}
result[field] = normalized;
}
}
return result;
}

/**
* Validate an array of console commands using the existing safety filter.
*/
function validateCommands(commands: unknown): string[] | undefined {
if (!Array.isArray(commands)) return undefined;
const validated: string[] = [];
for (const cmd of commands) {
if (typeof cmd === 'string') {
CommandValidator.validate(cmd); // throws on dangerous commands
validated.push(cmd);
}
}
return validated;
}

export async function handleMrqTools(
_action: string,
args: Record<string, unknown>,
tools: ITools
): Promise<Record<string, unknown>> {
const mrqAction = requireAction(args);

switch (mrqAction) {
case 'get_queue':
case 'get_job_config':
case 'get_cvars':
case 'get_output_settings':
case 'get_render_status':
case 'delete_job':
case 'render': {
const res = await executeAutomationRequest(
tools,
'manage_mrq',
{ ...args, action: mrqAction },
'MRQ automation bridge not available — ensure Movie Render Queue plugin is enabled'
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return cleanObject(res) as Record<string, unknown>;
}

case 'create_job':
case 'load_preset': {
// Normalize asset paths to canonical /Game/... form
const normalized = normalizePathFields(args, ['map', 'sequence', 'presetPath']);
const res = await executeAutomationRequest(
tools,
'manage_mrq',
{ ...normalized, action: mrqAction },
'MRQ automation bridge not available — ensure Movie Render Queue plugin is enabled'
);
return cleanObject(res) as Record<string, unknown>;
}

case 'set_cvars': {
const payload: Record<string, unknown> = { ...args, action: mrqAction };
if (args.cvars && typeof args.cvars === 'object' && !payload.set) {
payload.set = args.cvars;
}
// Validate start/end console commands through safety filter
if (payload.startCommands) {
payload.startCommands = validateCommands(payload.startCommands);
}
if (payload.endCommands) {
payload.endCommands = validateCommands(payload.endCommands);
}
const res = await executeAutomationRequest(
tools,
'manage_mrq',
payload,
'MRQ automation bridge not available'
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return cleanObject(res) as Record<string, unknown>;
}

case 'set_output_settings': {
const res = await executeAutomationRequest(
tools,
'manage_mrq',
{ ...args, action: mrqAction },
'MRQ automation bridge not available'
);
return cleanObject(res) as Record<string, unknown>;
}

default:
return {
success: false,
error: 'UNKNOWN_ACTION',
message: `Unknown MRQ action: ${mrqAction}. Available: 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`
};
}
}
Loading