UnrealPyMCP
A pure Python Unreal plugin containing an MCP-like server that lets an AI/LLM talk to Unreal by:
- Reading Unreal's Output Log
- Sending Python code to execute in Unreal
There are no pre-programmed commands, it fully relies on your AI executing Unreal Python commands, with no guardrails. There may be limits, since Unreal does not expose everything to Python.
_Asked GPT to create a small maze with cubes_
_Asked to create a new red material and apply it to actor MCP_Maze_6_6_
Project plugin that starts a local MCP-like HTTP server inside Unreal Editor so an AI client can:
- Read Unreal log output
- Execute Python in the running editor
This plugin is intentionally powerful. unreal_py_mcp/exec and unreal_py_mcp/exec_async run arbitrary Python in the editor process.
What It Starts
When the plugin is enabled, Unreal runs:
Content/Python/init_unreal.py
That imports unreal_py_mcp.py (primary implementation), which starts a server on:
http://127.0.0.1:3001(default)
Endpoints
GET /mcp
- Tool discovery - Includes meta.startup_guidance for MCP auto-detection and config hints
GET /mcp/help
- Built-in API docs (tools, limits, examples, endpoints)
GET /health
- Server and main-thread runner status - Includes current log resolution and startup guidance
GET /tasks/{task_id}/status
- Poll async execution task state/result (fallback when SSE is unavailable)
GET /tasks/{task_id}/stream
- Server-Sent Events (SSE) stream for live task events - Streams task status, progress, stdout/stderr, and tailed log lines
POST /mcp/messages
- Execute a tool with payload: {"tool": "...", "arguments": {...}}
Tools
unreal_py_mcp/get_logs
- Return last N log lines (default 500, max 5000)
unreal_py_mcp/get_log_path
- Return resolved log file and search paths
unreal_py_mcp/exec
- Execute Python synchronously on Unreal main thread - Supports timeout argument - Exposes report_progress(message, current=None, total=None) in execution context
unreal_py_mcp/exec_async
- Queue Python execution and return immediately with task_id - Stream live events via GET /tasks/{task_id}/stream - Poll GET /tasks/{task_id}/status as fallback
OpenCode Config
Example:
{
"mcp": {
"unreal_py_mcp": {
"type": "remote",
"url": "http://127.0.0.1:3001",
"enabled": true
}
}
}Request Examples
Get logs:
{
"tool": "unreal_py_mcp/get_logs",
"arguments": { "limit": 200 }
}Sync exec:
{
"tool": "unreal_py_mcp/exec",
"arguments": {
"code": "print('hello from unreal')",
"timeout": 60
}
}Async exec:
{
"tool": "unreal_py_mcp/exec_async",
"arguments": {
"code": "import time\nfor i in range(3):\n report_progress(f'step {i+1}', i+1, 3)\n time.sleep(1)\nresult={'done': True}",
"timeout": 60
}
}Then poll:
GET /tasks//status
Or stream live events:
curl -N http://127.0.0.1:3001/tasks//streamSSE event types currently emitted:
task_statusprogressstdoutstderrlog_linelog_infolog_errortask_result
Resume from a known event cursor:
- Query param:
GET /tasks//stream?cursor= - Or header:
Last-Event-ID:
Structured Errors
HTTP/API errors are returned as JSON:
{
"status": "error",
"error_type": "InvalidJson",
"message": "Invalid JSON payload: ...",
"timestamp": "2026-02-25T09:23:41.032949+00:00"
}Execution failures from unreal_py_mcp/exec / exec_async include:
error_type,message,stack_tracestdout,stderrrecent_logscontexttimestamp,timeout_seconds
Config Environment Variables
UNREAL_MCP_PORT
- Server port (default 3001)
UNREAL_MCP_LOG_PATH
- Absolute path to a specific .log file
UNREAL_MCP_EXEC_TIMEOUT
- Default sync/async execution timeout seconds (default 60)
UNREAL_MCP_MAX_EXEC_TIMEOUT
- Maximum allowed timeout seconds (default 300)
UNREAL_MCP_ERROR_LOG_LINES
- Number of log lines attached to exec error payloads (default 120)
UNREAL_MCP_TASK_EVENT_BUFFER
- Max buffered SSE events per task before oldest events are dropped (default 2000)
UNREAL_MCP_SSE_HEARTBEAT_SECONDS
- Idle heartbeat interval for SSE connections (default 15)
UNREAL_MCP_LOG_TAIL_POLL_SECONDS
- Poll interval for disk log tail streaming during async tasks (default 0.25)
UNREAL_MCP_DISABLE_SERVER
- Set to 1 to disable server startup
Log Path Resolution
Resolution order:
- Explicit
pathargument (tool call) UNREAL_MCP_LOG_PATH- `
/Saved/Logs/*.log` (preferred)
%LOCALAPPDATA%/UnrealEngine/*/Saved/Logs/*.log- `%LOCALAPPDATA%/
/Saved/Logs/*.log`
Notes on Async Behavior
- Async is non-blocking for the MCP client (you get a
task_idimmediately). - Unreal Python still executes on Unreal's main thread for editor safety.
- Use
exec_async+/streamfor real-time visibility on long jobs. - Keep
/statuspolling as a compatibility fallback.
Limitations
- Unreal editor API calls must run on Unreal's main thread.
exec_asyncimproves client responsiveness, but Unreal execution remains serialized on the main thread.- Live log streaming is based on tailing the resolved log file on disk (not Unreal in-memory log hooks).
Troubleshooting
- If
/mcpis down:
- verify plugin enabled - verify Python plugin enabled - restart Unreal Editor
- If logs cannot be resolved:
- call unreal_py_mcp/get_log_path - set UNREAL_MCP_LOG_PATH
- For capability/introspection:
- call /mcp/help and /health
