gengine's system architecture — C++ modules, MCP wire format, REST API endpoints, task queue states, and the SSE event broadcaster.
Architecture
gengine is a multi-module Unreal Engine plugin with a companion Node.js bridge. This page describes how the components fit together, how data flows from an AI tool call to the editor and back, and what each C++ module is responsible for.
System Diagram
┌─────────────────────────────────────────────────────────────────┐
│ AI Client (Claude CLI / OpenAI / Ollama / Web Chat Panel) │
└────────────────────────┬────────────────────────────────────────┘
│ MCP wire protocol (stdio or HTTP+SSE)
▼
┌─────────────────────────────────────────────────────────────────┐
│ MCP Bridge (Node.js — Resources/mcp-bridge/) │
│ ┌─────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Skill Router │ │ Tool Cache │ │ Context Loader│ │
│ │ routes ops to │ │ caches tool │ │ injects UE │ │
│ │ REST endpoints │ │ definitions │ │ context docs │ │
│ └────────┬────────┘ └──────────────────┘ └───────────────┘ │
└───────────┼─────────────────────────────────────────────────────┘
│ HTTP REST (localhost:8080)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Unreal Engine Plugin (C++) │
│ │
│ ┌──────────────────┐ ┌─────────────────────────────────────┐ │
│ │ GengineMCPServer │ │ GengineSubsystem │ │
│ │ HTTP listener │──▶│ lifecycle, settings, licensing │ │
│ │ request routing │ └─────────────────────────────────────┘ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────────────────────────┐│
│ │ MCPAutoRouter │ │ MCPParamValidator ││
│ │ dispatches ops │──▶│ type checks, path traversal, ││
│ │ to tool handlers │ │ numeric validation, blocklists ││
│ └────────┬─────────┘ └──────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────────────────────────┐│
│ │ MCPTaskQueue │ │ MCPToolRegistry ││
│ │ async dispatch │ │ FMCPToolBase registry, ││
│ │ max 4 concurrent │ │ tool discovery, annotation cache ││
│ └────────┬─────────┘ └──────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐│
│ │ Tool Handlers (Source/Gengine/Private/MCP/Tools/) ││
│ │ MCPTool_World MCPTool_Assets MCPTool_Blueprints ││
│ │ MCPTool_Animation MCPTool_Character MCPTool_InputMat ││
│ │ MCPTool_Status MCPTool_Context MCPTool_Nativize ││
│ └────────┬─────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────────────────────────┐│
│ │ MCPOutputSanitiz │ │ SSE Event Broadcaster ││
│ │ strips injection │ │ pushes task progress to ││
│ │ patterns, paths │ │ connected HTTP+SSE clients ││
│ └──────────────────┘ └──────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
C++ Module Reference
gengine is split into 4 C++ modules for compile-time dependency isolation:
| Module | Build Target | Responsibility |
|---|---|---|
Gengine | Runtime | Core MCP server, tool registry, task queue, auto-router, validators, all tool handlers |
GengineAutonomy | Runtime | Checkpoint system, shadow git manager, autonomous task recovery |
GengineChatGateway | Runtime | Chat provider abstraction layer — Anthropic, OpenAI, Ollama adapters |
GengineMCPStreamable | Runtime | HTTP+SSE transport — SSE handler, streamable MCP endpoint, event broadcaster |
Module dependencies
GengineMCPStreamable
└── Gengine
└── GengineAutonomy
GengineChatGateway
└── (standalone — no plugin-internal deps)
GengineChatGateway is intentionally isolated so provider adapters can be updated without touching the MCP layer.
MCP Wire Format
gengine implements MCP 1.0. Messages are JSON-RPC 2.0 envelopes:
Tool call (client → server)
{
"jsonrpc": "2.0",
"id": "req-42",
"method": "tools/call",
"params": {
"name": "unreal_assets",
"arguments": {
"operation": "search",
"params": {
"query": "BP_Enemy",
"asset_type": "Blueprint"
}
}
}
}
Tool result (server → client)
{
"jsonrpc": "2.0",
"id": "req-42",
"result": {
"content": [
{
"type": "text",
"text": "{\"assets\":[{\"name\":\"BP_Enemy_Goblin\",\"path\":\"/Game/Characters/Enemies/BP_Enemy_Goblin\",\"type\":\"Blueprint\"}],\"total\":1}"
}
],
"isError": false
}
}
Error result
{
"jsonrpc": "2.0",
"id": "req-42",
"result": {
"content": [
{
"type": "text",
"text": "Validation error: parameter 'query' is required"
}
],
"isError": true
}
}
REST API Endpoints
The gengine REST API on port 8080 is the interface between the MCP bridge and the C++ plugin:
| Method | Path | Description |
|---|---|---|
POST | /mcp/tool | Execute a tool call. Body: { name, operation, params } |
GET | /mcp/tools | List all registered tool definitions (JSON Schema) |
GET | /mcp/status | Plugin health, version, connected clients |
GET | /mcp/tasks | List active and recent tasks with state |
GET | /mcp/tasks/{id} | Get full detail for a specific task |
DELETE | /mcp/tasks/{id} | Cancel a running task |
GET | /mcp/activity | Recent tool call activity log |
GET | /mcp/sse | SSE stream for real-time task progress events |
POST | /mcp/license/activate | Activate a license key |
GET | /mcp/license/status | Current license tier and seat info |
Tool call request body
{
"name": "unreal_blueprints",
"operation": "add_variable",
"params": {
"path": "/Game/Characters/BP_Player",
"name": "Health",
"type": "float",
"default_value": 100.0
}
}
Task Queue States
Each task in the async queue passes through these states:
Queued ──▶ Running ──▶ Completed
└──▶ Failed
└──▶ Cancelled (via DELETE /mcp/tasks/{id})
Queued ──▶ TimedOut (after 30s without executing)
The task queue enforces the 4-concurrent-task limit. Tasks in Queued state wait until a Running slot opens.
Task object shape
{
"id": "task-8f3a2c",
"tool": "unreal_blueprints",
"operation": "add_node",
"state": "Completed",
"queued_at": "2025-01-15T14:32:07Z",
"started_at": "2025-01-15T14:32:07Z",
"completed_at": "2025-01-15T14:32:08Z",
"duration_ms": 412,
"result": { "node_id": "node_7", "success": true }
}
SSE Event Broadcaster
The GengineMCPStreamable module pushes real-time events to connected HTTP+SSE clients. Events are emitted for every task state transition:
event: task_queued
data: {"id":"task-8f3a2c","tool":"unreal_blueprints","operation":"add_node"}
event: task_started
data: {"id":"task-8f3a2c","queued_ms":12}
event: task_progress
data: {"id":"task-8f3a2c","message":"Locating graph 'EventGraph'","percent":40}
event: task_completed
data: {"id":"task-8f3a2c","duration_ms":412,"result":{"node_id":"node_7"}}
The Web Chat Panel subscribes to this SSE stream to show live task progress in the Tasks tab.
Key Source Directories
Source/
├── Gengine/
│ ├── Private/
│ │ ├── MCP/
│ │ │ ├── GengineMCPServer.cpp HTTP server, request parsing
│ │ │ ├── MCPAutoRouter.cpp Operation dispatch, safety tier enforcement
│ │ │ ├── MCPTaskQueue.cpp Async task queue, concurrency management
│ │ │ ├── MCPToolRegistry.cpp Tool registration, discovery endpoint
│ │ │ ├── MCPParamValidator.cpp Input validation, injection prevention
│ │ │ ├── MCPOutputSanitizer.cpp Response sanitization
│ │ │ └── Tools/
│ │ │ ├── MCPTool_World.cpp
│ │ │ ├── MCPTool_Assets.cpp
│ │ │ ├── MCPTool_Blueprints.cpp
│ │ │ ├── MCPTool_Animation.cpp
│ │ │ ├── MCPTool_Character.cpp
│ │ │ ├── MCPTool_InputMaterials.cpp
│ │ │ ├── MCPTool_Status.cpp
│ │ │ ├── MCPTool_Context.cpp
│ │ │ └── MCPTool_Nativize.cpp
│ │ └── GengineSubsystem.cpp Plugin lifecycle, settings, licensing
│ └── Public/
│ ├── GengineSettings.h
│ └── GengineConstants.h
├── GengineAutonomy/
│ └── Private/Checkpoint/
│ └── ShadowGitManager.cpp
├── GengineChatGateway/
│ └── Private/ChatGatewayProvider.cpp
└── GengineMCPStreamable/
├── Private/MCPSSEHandler.cpp
└── Private/MCPStreamableHandler.cpp
Resources/
└── mcp-bridge/
├── src/
│ ├── index.ts Entry point, transport setup
│ ├── router.ts Skill tree router
│ ├── toolCache.ts Tool definition cache
│ └── contextLoader.ts UE context doc injector
└── dist/ Compiled JS output