@createlex/createlexgenai 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,272 @@
1
+ # @createlex/createlexgenai
2
+
3
+ CLI tool for integrating AI-powered tools with Unreal Engine. Works as an MCP server for AI CLI tools (Claude Code, Codex CLI, Gemini CLI, etc.) or as a standalone command-line interface.
4
+
5
+ **71+ tools** for spawning actors, creating Blueprints, managing materials, UI widgets, physics, project architecture, and more — all without requiring a custom UE plugin.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # Install globally
11
+ npm install -g @createlex/createlexgenai
12
+
13
+ # Authenticate
14
+ createlex login
15
+
16
+ # Check everything is working
17
+ createlex status
18
+
19
+ # Set up your AI tool
20
+ createlex setup claude-code
21
+ ```
22
+
23
+ ## Requirements
24
+
25
+ - **Node.js 18+**
26
+ - **Unreal Engine 5.x** with at least one backend enabled (see below)
27
+ - **Python 3.10+** (for MCP serve mode)
28
+ - **CreateLex account** with active subscription
29
+
30
+ ## Setup Guide (No Plugin Required)
31
+
32
+ ### Step 1: Install the CLI
33
+
34
+ ```bash
35
+ npm install -g @createlex/createlexgenai
36
+ ```
37
+
38
+ Or run without installing:
39
+
40
+ ```bash
41
+ npx @createlex/createlexgenai --version
42
+ ```
43
+
44
+ ### Step 2: Authenticate
45
+
46
+ ```bash
47
+ createlex login
48
+ ```
49
+
50
+ This opens your browser to sign in. Your token is saved to `~/.createlex/auth.json`.
51
+
52
+ Verify:
53
+
54
+ ```bash
55
+ createlex status
56
+ ```
57
+
58
+ ### Step 3: Enable UE Built-in Backends
59
+
60
+ You need **at least one** of these enabled in your Unreal project. Both are built into UE — no custom plugin required.
61
+
62
+ #### Option A: Web Remote Control (Recommended)
63
+
64
+ 1. Open your UE project
65
+ 2. Go to **Edit > Plugins**
66
+ 3. Search for **"Web Remote Control"** and enable it
67
+ 4. Restart the editor
68
+ 5. Go to **Edit > Project Settings > Plugins > Remote Control**
69
+ 6. Check **"Enable Remote Control Web Server"**
70
+ 7. Set port to **30010** (default)
71
+ 8. Ensure **"Enable HTTP Server"** is checked
72
+
73
+ #### Option B: Python Remote Execution
74
+
75
+ 1. Open your UE project
76
+ 2. Go to **Edit > Plugins**
77
+ 3. Search for **"Python Editor Script Plugin"** and enable it
78
+ 4. Restart the editor
79
+ 5. Go to **Edit > Project Settings > Plugins > Python**
80
+ 6. Check **"Enable Remote Execution"**
81
+ 7. Multicast group: `239.0.0.1`, port: `6766` (defaults)
82
+
83
+ > **Tip:** Enable both for maximum reliability. If one backend is unavailable, the CLI automatically falls back to the other.
84
+
85
+ ### Step 4: Verify Connection
86
+
87
+ ```bash
88
+ createlex connect
89
+ ```
90
+
91
+ Expected output (without plugin):
92
+
93
+ ```
94
+ Plugin (TCP 9878): Not connected
95
+ Web Remote Control (HTTP 30010): Connected
96
+ Remote Execution (UDP 6766): Available
97
+ ```
98
+
99
+ ### Step 5: Configure Your AI Tool / IDE
100
+
101
+ #### Automatic Setup
102
+
103
+ ```bash
104
+ # Set up a specific IDE
105
+ createlex setup <ide>
106
+
107
+ # Set up all detected IDEs at once
108
+ createlex setup --all
109
+ ```
110
+
111
+ #### Supported IDEs
112
+
113
+ | IDE | Command | Config Location |
114
+ |---|---|---|
115
+ | Claude Code | `createlex setup claude-code` | CLI command (manual) |
116
+ | Cursor | `createlex setup cursor` | `~/.cursor/mcp.json` |
117
+ | Windsurf | `createlex setup windsurf` | `~/.codeium/windsurf/mcp_config.json` |
118
+ | Claude Desktop | `createlex setup claude-desktop` | `%APPDATA%/Claude/claude_desktop_config.json` |
119
+ | VS Code | `createlex setup vscode` | `.vscode/settings.json` |
120
+ | Antigravity | `createlex setup antigravity` | `~/.gemini/antigravity/mcp_config.json` |
121
+ | Gemini CLI | `createlex setup gemini-cli` | `~/.gemini/settings.json` |
122
+ | Kiro | `createlex setup kiro` | `~/.kiro/settings/mcp.json` |
123
+ | Trae | `createlex setup trae` | `%APPDATA%/Trae/User/mcp.json` |
124
+ | Augment | `createlex setup augment` | `~/.augment/mcp.json` |
125
+ | Codex | `createlex setup codex` | `~/.codex/config.toml` |
126
+
127
+ #### Manual IDE Configuration
128
+
129
+ All IDEs use the same MCP server command:
130
+
131
+ ```
132
+ npx @createlex/createlexgenai serve
133
+ ```
134
+
135
+ **Claude Code:**
136
+ ```bash
137
+ claude mcp add createlex-unreal -- npx @createlex/createlexgenai serve
138
+ ```
139
+
140
+ **Codex CLI** (`~/.codex/config.toml`):
141
+ ```toml
142
+ [mcp_servers.createlex-unreal]
143
+ command = "npx"
144
+ args = ["@createlex/createlexgenai", "serve"]
145
+ ```
146
+
147
+ **JSON-based IDEs** (Cursor, Windsurf, Claude Desktop, etc.):
148
+ ```json
149
+ {
150
+ "mcpServers": {
151
+ "createlex-unreal": {
152
+ "command": "npx",
153
+ "args": ["@createlex/createlexgenai", "serve"]
154
+ }
155
+ }
156
+ }
157
+ ```
158
+
159
+ ### Step 6: Start Using It
160
+
161
+ Once configured, your AI tool has access to all 71+ Unreal Engine tools. Try:
162
+
163
+ - *"Spawn a cube at the origin"*
164
+ - *"Create a Blueprint called BP_Enemy based on Character"*
165
+ - *"List all actors in the scene"*
166
+ - *"Create a medieval town"*
167
+
168
+ ## CLI Commands
169
+
170
+ ```bash
171
+ # MCP server mode (for AI CLI tools)
172
+ createlex serve # stdio MCP server
173
+ createlex serve --port 38080 # TCP MCP server
174
+
175
+ # Direct tool execution
176
+ createlex exec <tool> [--key value ...]
177
+ createlex exec get_all_scene_objects
178
+ createlex exec spawn_object --actor-class StaticMeshActor --location "0,0,0"
179
+ createlex exec execute_python_script --script "print('Hello from UE!')"
180
+
181
+ # List available tools
182
+ createlex tools # Formatted list
183
+ createlex tools --json # JSON output
184
+
185
+ # Auth
186
+ createlex login # Browser auth flow
187
+ createlex logout # Clear credentials
188
+
189
+ # Status & connection
190
+ createlex status # Auth + subscription + UE connection info
191
+ createlex connect # Test UE connection, show setup tips
192
+
193
+ # Configuration
194
+ createlex config list # Show all settings
195
+ createlex config get unrealPort # Get a specific value
196
+ createlex config set unrealPort 9879
197
+ createlex config reset # Reset to defaults
198
+
199
+ # IDE setup
200
+ createlex setup # List supported IDEs
201
+ createlex setup claude-code # Set up specific IDE
202
+ createlex setup --all # Set up all detected IDEs
203
+ ```
204
+
205
+ ## How Connection Fallback Works
206
+
207
+ When a tool is executed, the CLI tries backends in this order:
208
+
209
+ 1. **CreatelexGenAI Plugin** (TCP port 9878) — Fastest, direct JSON. Used if the plugin is installed.
210
+ 2. **Web Remote Control** (HTTP port 30010) — Translates commands to Python, executes via UE's built-in HTTP API.
211
+ 3. **Python Remote Execution** (UDP port 6766) — Translates commands to Python, executes via UE's multicast remote execution protocol.
212
+
213
+ All 71 tools work through all backends. The `ue_native_handler.py` module translates every command into standalone Python that runs inside UE's interpreter.
214
+
215
+ ## Available Tools (71+)
216
+
217
+ | Category | Tools |
218
+ |---|---|
219
+ | **General** | `get_all_scene_objects`, `execute_python_script`, `execute_unreal_command` |
220
+ | **Script** | `execute_python_script`, `run_automation_test` |
221
+ | **Actors** | `spawn_object`, `delete_actor`, `find_actors_by_name`, `set_actor_transform`, `set_actor_property`, `get_actor_properties`, `duplicate_actor`, `rename_actor`, `set_actor_mobility`, `set_actor_physics` |
222
+ | **Blueprint** | `create_blueprint`, `spawn_blueprint`, `compile_blueprint`, `add_component_to_blueprint`, `set_blueprint_variable`, `get_blueprint_details` |
223
+ | **Blueprint Nodes** | `add_node`, `connect_nodes`, `add_variable`, `add_function`, `add_event`, `set_pin_value`, `get_blueprint_graph` |
224
+ | **Materials** | `create_material`, `apply_material_to_actor`, `set_material_parameter`, `create_material_instance` |
225
+ | **Mesh** | `set_static_mesh`, `create_procedural_mesh`, `merge_meshes` |
226
+ | **Physics** | `simulate_physics`, `add_physics_constraint`, `set_collision_profile`, `add_force`, `set_physics_material` |
227
+ | **UI/Widgets** | `create_widget`, `modify_widget`, `bind_widget_event`, `add_widget_animation`, `create_widget_component` |
228
+ | **UI Slicing** | `upload_ui_image`, `slice_ui_image`, `generate_ui_from_slices` |
229
+ | **Project** | `create_project_folder`, `get_files_in_folder`, `import_asset`, `create_level`, `set_level_settings` |
230
+ | **Architecture** | `create_town`, `construct_house`, `construct_building`, `create_road`, `create_landscape`, `place_foliage`, `create_water_body`, `create_sky_atmosphere`, `create_lighting_scenario`, `create_marketplace`, `create_wall`, `create_floor` |
231
+
232
+ ## Configuration
233
+
234
+ Settings are stored in `~/.createlex/config.json`:
235
+
236
+ | Key | Default | Description |
237
+ |---|---|---|
238
+ | `unrealPort` | `9878` | CreatelexGenAI plugin TCP port |
239
+ | `mcpPort` | `38080` | MCP TCP server port (for `serve --port`) |
240
+ | `apiBaseUrl` | `https://api.createlex.com/api` | CreateLex API endpoint |
241
+ | `webBaseUrl` | `https://createlex.com` | CreateLex web app URL |
242
+ | `debug` | `false` | Enable debug logging |
243
+
244
+ ## Troubleshooting
245
+
246
+ ### "No backends connected to Unreal Engine"
247
+ - Make sure UE is running with your project open
248
+ - Enable Web Remote Control or Python Remote Execution (see Step 3)
249
+ - Run `createlex connect` for detailed diagnostics
250
+
251
+ ### "Authentication: Not logged in"
252
+ ```bash
253
+ createlex login
254
+ ```
255
+
256
+ ### "Subscription: Inactive"
257
+ - Visit [createlex.com](https://createlex.com) to check your subscription status
258
+ - Run `createlex status` to see the specific error
259
+
260
+ ### Web Remote Control not responding
261
+ - Check **Edit > Project Settings > Plugins > Remote Control** in UE
262
+ - Ensure "Enable Remote Control Web Server" is checked
263
+ - Verify port 30010 is not blocked by firewall
264
+
265
+ ### Python Remote Execution not available
266
+ - Check **Edit > Project Settings > Plugins > Python** in UE
267
+ - Ensure "Enable Remote Execution" is checked
268
+ - Verify UDP port 6766 is not blocked
269
+
270
+ ## License
271
+
272
+ Proprietary. Requires an active CreateLex subscription.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { run } = require('../src/cli');
5
+ run(process.argv);
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@createlex/createlexgenai",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool and MCP server for CreatelexGenAI — Unreal Engine AI integration",
5
+ "bin": {
6
+ "createlex": "./bin/createlex.js"
7
+ },
8
+ "main": "src/cli.js",
9
+ "scripts": {
10
+ "start": "node bin/createlex.js",
11
+ "test": "echo \"No tests yet\" && exit 0"
12
+ },
13
+ "keywords": [
14
+ "createlex",
15
+ "unreal-engine",
16
+ "mcp",
17
+ "ai",
18
+ "cli",
19
+ "generative-ai",
20
+ "game-development"
21
+ ],
22
+ "author": "CreateLex LLC",
23
+ "license": "MIT",
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "dependencies": {
28
+ "axios": "^1.6.0",
29
+ "chalk": "^4.1.2",
30
+ "commander": "^12.1.0",
31
+ "open": "^8.4.2",
32
+ "ora": "^5.4.1"
33
+ },
34
+ "files": [
35
+ "bin/",
36
+ "src/",
37
+ "python/",
38
+ "README.md"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/CreatelexLLC/createlexgenai"
43
+ },
44
+ "homepage": "https://createlex.com"
45
+ }
@@ -0,0 +1,280 @@
1
+ """
2
+ MCP Activity Tracker - Logs tool usage to the CreateLex backend
3
+
4
+ This module tracks MCP tool calls and sends usage data to the backend
5
+ for analytics and monitoring. All logging is non-blocking to avoid
6
+ impacting tool execution performance.
7
+ """
8
+
9
+ import os
10
+ import json
11
+ import time
12
+ import threading
13
+ import queue
14
+ from datetime import datetime
15
+ from typing import Optional, Dict, Any
16
+ import logging
17
+
18
+ logger = logging.getLogger('mcp_activity_tracker')
19
+
20
+ # Configuration
21
+ ACTIVITY_API_ENDPOINT = os.environ.get('CREATELEX_API_URL', 'https://api.createlex.com') + '/api/mcp/activity'
22
+ BATCH_SIZE = 10 # Send activities in batches
23
+ FLUSH_INTERVAL = 30 # Flush every 30 seconds even if batch isn't full
24
+ MAX_QUEUE_SIZE = 100 # Maximum pending activities before dropping
25
+
26
+ class MCPActivityTracker:
27
+ """
28
+ Asynchronous activity tracker for MCP tool calls.
29
+
30
+ Collects tool usage data and sends it to the backend in batches
31
+ to minimize network overhead and avoid blocking tool execution.
32
+ """
33
+
34
+ def __init__(self):
35
+ self._activity_queue = queue.Queue(maxsize=MAX_QUEUE_SIZE)
36
+ self._worker_thread = None
37
+ self._shutdown = False
38
+ self._session_id = self._generate_session_id()
39
+ self._session_start = datetime.utcnow().isoformat()
40
+ self._tool_call_count = 0
41
+
42
+ # User info from environment (set by VSCode extension)
43
+ self._user_id = os.environ.get('CREATELEX_USER_ID')
44
+ self._user_email = os.environ.get('CREATELEX_USER_EMAIL') or os.environ.get('USER_EMAIL')
45
+ self._device_id = os.environ.get('CREATELEX_DEVICE_ID')
46
+ self._auth_token = os.environ.get('CREATELEX_AUTH_TOKEN')
47
+
48
+ # Start background worker
49
+ self._start_worker()
50
+
51
+ logger.info(f"Activity tracker initialized - Session: {self._session_id[:8]}...")
52
+
53
+ def _generate_session_id(self) -> str:
54
+ """Generate a unique session ID"""
55
+ import hashlib
56
+ import uuid
57
+ return hashlib.sha256(f"{uuid.uuid4()}-{time.time()}".encode()).hexdigest()[:32]
58
+
59
+ def _start_worker(self):
60
+ """Start the background worker thread"""
61
+ if self._worker_thread is None or not self._worker_thread.is_alive():
62
+ self._worker_thread = threading.Thread(target=self._worker_loop, daemon=True)
63
+ self._worker_thread.start()
64
+ logger.info("Activity tracker worker started")
65
+
66
+ def _worker_loop(self):
67
+ """Background worker that sends batched activities"""
68
+ batch = []
69
+ last_flush = time.time()
70
+
71
+ while not self._shutdown:
72
+ try:
73
+ # Try to get an activity from the queue (with timeout)
74
+ try:
75
+ activity = self._activity_queue.get(timeout=1.0)
76
+ batch.append(activity)
77
+ except queue.Empty:
78
+ pass
79
+
80
+ # Check if we should flush the batch
81
+ should_flush = (
82
+ len(batch) >= BATCH_SIZE or
83
+ (len(batch) > 0 and time.time() - last_flush >= FLUSH_INTERVAL)
84
+ )
85
+
86
+ if should_flush:
87
+ self._send_batch(batch)
88
+ batch = []
89
+ last_flush = time.time()
90
+
91
+ except Exception as e:
92
+ logger.error(f"Activity worker error: {e}")
93
+ time.sleep(1) # Back off on error
94
+
95
+ # Final flush on shutdown
96
+ if batch:
97
+ self._send_batch(batch)
98
+
99
+ def _send_batch(self, batch: list):
100
+ """Send a batch of activities to the backend"""
101
+ if not batch:
102
+ return
103
+
104
+ try:
105
+ import requests
106
+
107
+ payload = {
108
+ 'session_id': self._session_id,
109
+ 'user_id': self._user_id,
110
+ 'user_email': self._user_email,
111
+ 'device_id': self._device_id,
112
+ 'activities': batch,
113
+ 'timestamp': datetime.utcnow().isoformat()
114
+ }
115
+
116
+ headers = {
117
+ 'Content-Type': 'application/json'
118
+ }
119
+
120
+ # Add auth token if available
121
+ if self._auth_token:
122
+ headers['Authorization'] = f'Bearer {self._auth_token}'
123
+
124
+ response = requests.post(
125
+ ACTIVITY_API_ENDPOINT,
126
+ json=payload,
127
+ headers=headers,
128
+ timeout=5 # Short timeout to avoid blocking
129
+ )
130
+
131
+ if response.status_code == 200:
132
+ logger.debug(f"Sent {len(batch)} activities to backend")
133
+ else:
134
+ logger.warning(f"Failed to send activities: {response.status_code}")
135
+
136
+ except requests.RequestException as e:
137
+ logger.warning(f"Network error sending activities: {e}")
138
+ except Exception as e:
139
+ logger.error(f"Error sending activities: {e}")
140
+
141
+ def track_tool_call(
142
+ self,
143
+ tool_name: str,
144
+ arguments: Dict[str, Any],
145
+ success: bool,
146
+ duration_ms: float,
147
+ error: Optional[str] = None,
148
+ result_summary: Optional[str] = None
149
+ ):
150
+ """
151
+ Track a tool call.
152
+
153
+ Args:
154
+ tool_name: Name of the MCP tool called
155
+ arguments: Arguments passed to the tool (sanitized)
156
+ success: Whether the tool call succeeded
157
+ duration_ms: Execution time in milliseconds
158
+ error: Error message if failed
159
+ result_summary: Brief summary of the result (for analytics)
160
+ """
161
+ self._tool_call_count += 1
162
+
163
+ activity = {
164
+ 'type': 'mcp_tool_call',
165
+ 'tool_name': tool_name,
166
+ 'success': success,
167
+ 'duration_ms': duration_ms,
168
+ 'timestamp': datetime.utcnow().isoformat(),
169
+ 'call_number': self._tool_call_count,
170
+ # Sanitize arguments - remove potentially sensitive data
171
+ 'argument_keys': list(arguments.keys()) if arguments else [],
172
+ 'argument_count': len(arguments) if arguments else 0
173
+ }
174
+
175
+ if error:
176
+ activity['error'] = str(error)[:200] # Truncate long errors
177
+
178
+ if result_summary:
179
+ activity['result_summary'] = result_summary[:100]
180
+
181
+ try:
182
+ self._activity_queue.put_nowait(activity)
183
+ except queue.Full:
184
+ logger.warning("Activity queue full, dropping oldest entry")
185
+ try:
186
+ self._activity_queue.get_nowait()
187
+ self._activity_queue.put_nowait(activity)
188
+ except:
189
+ pass
190
+
191
+ def track_session_event(self, event_type: str, details: Optional[Dict] = None):
192
+ """
193
+ Track a session event (startup, shutdown, error, etc.)
194
+
195
+ Args:
196
+ event_type: Type of event ('session_start', 'session_end', 'error', etc.)
197
+ details: Additional event details
198
+ """
199
+ activity = {
200
+ 'type': 'session_event',
201
+ 'event_type': event_type,
202
+ 'timestamp': datetime.utcnow().isoformat(),
203
+ 'details': details or {}
204
+ }
205
+
206
+ try:
207
+ self._activity_queue.put_nowait(activity)
208
+ except queue.Full:
209
+ pass
210
+
211
+ def get_session_stats(self) -> Dict:
212
+ """Get current session statistics"""
213
+ return {
214
+ 'session_id': self._session_id,
215
+ 'session_start': self._session_start,
216
+ 'tool_call_count': self._tool_call_count,
217
+ 'user_id': self._user_id,
218
+ 'device_id': self._device_id,
219
+ 'pending_activities': self._activity_queue.qsize()
220
+ }
221
+
222
+ def shutdown(self):
223
+ """Graceful shutdown - flush pending activities"""
224
+ logger.info("Activity tracker shutting down...")
225
+ self._shutdown = True
226
+
227
+ # Track session end
228
+ self.track_session_event('session_end', {
229
+ 'total_tool_calls': self._tool_call_count
230
+ })
231
+
232
+ # Wait for worker to finish (with timeout)
233
+ if self._worker_thread and self._worker_thread.is_alive():
234
+ self._worker_thread.join(timeout=5)
235
+
236
+
237
+ # Global tracker instance
238
+ _tracker: Optional[MCPActivityTracker] = None
239
+
240
+ def get_tracker() -> MCPActivityTracker:
241
+ """Get or create the global activity tracker"""
242
+ global _tracker
243
+ if _tracker is None:
244
+ _tracker = MCPActivityTracker()
245
+ _tracker.track_session_event('session_start', {
246
+ 'platform': os.environ.get('MCP_PLATFORM', 'unknown'),
247
+ 'version': os.environ.get('MCP_VERSION', 'unknown')
248
+ })
249
+ return _tracker
250
+
251
+ def track_tool_call(
252
+ tool_name: str,
253
+ arguments: Dict[str, Any],
254
+ success: bool,
255
+ duration_ms: float,
256
+ error: Optional[str] = None,
257
+ result_summary: Optional[str] = None
258
+ ):
259
+ """Convenience function to track a tool call"""
260
+ try:
261
+ tracker = get_tracker()
262
+ tracker.track_tool_call(
263
+ tool_name=tool_name,
264
+ arguments=arguments,
265
+ success=success,
266
+ duration_ms=duration_ms,
267
+ error=error,
268
+ result_summary=result_summary
269
+ )
270
+ except Exception as e:
271
+ # Never let tracking errors affect main functionality
272
+ logger.error(f"Failed to track tool call: {e}")
273
+
274
+ def shutdown_tracker():
275
+ """Shutdown the global tracker"""
276
+ global _tracker
277
+ if _tracker:
278
+ _tracker.shutdown()
279
+ _tracker = None
280
+