iflow-mcp_bethington-cheat-engine-server-python 0.1.0__py3-none-any.whl

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.
Files changed (40) hide show
  1. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/METADATA +16 -0
  2. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/RECORD +40 -0
  3. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/WHEEL +5 -0
  4. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/top_level.txt +1 -0
  7. server/cheatengine/__init__.py +19 -0
  8. server/cheatengine/ce_bridge.py +1670 -0
  9. server/cheatengine/lua_interface.py +460 -0
  10. server/cheatengine/table_parser.py +1221 -0
  11. server/config/__init__.py +20 -0
  12. server/config/settings.py +347 -0
  13. server/config/whitelist.py +378 -0
  14. server/gui_automation/__init__.py +43 -0
  15. server/gui_automation/core/__init__.py +8 -0
  16. server/gui_automation/core/integration.py +951 -0
  17. server/gui_automation/demos/__init__.py +8 -0
  18. server/gui_automation/demos/basic_demo.py +754 -0
  19. server/gui_automation/demos/notepad_demo.py +460 -0
  20. server/gui_automation/demos/simple_demo.py +319 -0
  21. server/gui_automation/tools/__init__.py +8 -0
  22. server/gui_automation/tools/mcp_tools.py +974 -0
  23. server/main.py +519 -0
  24. server/memory/__init__.py +0 -0
  25. server/memory/analyzer.py +0 -0
  26. server/memory/reader.py +0 -0
  27. server/memory/scanner.py +0 -0
  28. server/memory/symbols.py +0 -0
  29. server/process/__init__.py +16 -0
  30. server/process/launcher.py +608 -0
  31. server/process/manager.py +185 -0
  32. server/process/monitors.py +202 -0
  33. server/process/permissions.py +131 -0
  34. server/process_whitelist.json +119 -0
  35. server/pyautogui/__init__.py +0 -0
  36. server/utils/__init__.py +37 -0
  37. server/utils/data_types.py +368 -0
  38. server/utils/formatters.py +430 -0
  39. server/utils/validators.py +340 -0
  40. server/window_automation/__init__.py +59 -0
server/main.py ADDED
@@ -0,0 +1,519 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import sys
5
+ import argparse
6
+ import asyncio
7
+ import logging
8
+ import time
9
+ import uuid
10
+ from pathlib import Path
11
+ from typing import Dict, Any, List, Optional
12
+
13
+ from mcp.server.fastmcp import FastMCP
14
+ from mcp.types import Tool
15
+
16
+ # Import our modules
17
+ from server.process.manager import ProcessManager
18
+ from server.process.launcher import ApplicationLauncher
19
+ from server.utils.validators import validate_address, validate_size
20
+ from server.utils.formatters import format_process_info
21
+ from server.config.settings import ServerConfig
22
+ from server.config.whitelist import ProcessWhitelist
23
+
24
+ # Try to import Windows-specific modules (will fail on Linux)
25
+ try:
26
+ from server.cheatengine.table_parser import CheatTableParser
27
+ from server.cheatengine.ce_bridge import CheatEngineBridge
28
+ CE_ENGINE_AVAILABLE = True
29
+ except ImportError as e:
30
+ CE_ENGINE_AVAILABLE = False
31
+ CheatTableParser = None
32
+ CheatEngineBridge = None
33
+ logger = logging.getLogger(__name__)
34
+ logger.warning(f"Cheat Engine modules not available (Windows only): {e}")
35
+
36
+ import glob
37
+
38
+ # Configure logging
39
+ logging.basicConfig(
40
+ level=logging.INFO,
41
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
42
+ )
43
+ logger = logging.getLogger(__name__)
44
+
45
+ # Import automation tools for integration
46
+ try:
47
+ import sys
48
+ parent_dir = Path(__file__).parent.parent
49
+ if str(parent_dir) not in sys.path:
50
+ sys.path.insert(0, str(parent_dir))
51
+ from automation_tools import register_automation_tools
52
+ from gui_automation.tools.mcp_tools import ALL_PYAUTOGUI_TOOLS, PyAutoGUIToolHandler
53
+ from window_automation.tools.mcp_tools import ALL_PYWINAUTO_TOOLS, PyWinAutoToolHandler
54
+ AUTOMATION_AVAILABLE = True
55
+ PYAUTOGUI_AVAILABLE = True
56
+ PYWINAUTO_AVAILABLE = True
57
+ logger.info("Automation tools, PyAutoGUI, and PyWinAuto modules loaded successfully")
58
+ except ImportError as e:
59
+ AUTOMATION_AVAILABLE = False
60
+ PYAUTOGUI_AVAILABLE = False
61
+ PYWINAUTO_AVAILABLE = False
62
+ logger.warning(f"Automation tools not available: {e}")
63
+ except ImportError as e:
64
+ logger.warning(f"Automation tools not available: {e}")
65
+ AUTOMATION_AVAILABLE = False
66
+
67
+ # Parse command line arguments
68
+ parser = argparse.ArgumentParser(description="MCP Cheat Engine Server")
69
+ parser.add_argument(
70
+ "--config",
71
+ default="config.json",
72
+ help="Configuration file path"
73
+ )
74
+ parser.add_argument("--debug", action="store_true", help="Enable debug mode")
75
+ parser.add_argument("--read-only", action="store_true", help="Enable read-only mode")
76
+ args = parser.parse_args()
77
+
78
+ # Initialize server
79
+ mcp = FastMCP("cheat-engine-server")
80
+
81
+ # Global state
82
+ server_config = ServerConfig()
83
+ process_manager = ProcessManager()
84
+ process_whitelist = ProcessWhitelist()
85
+
86
+ # Initialize application launcher
87
+ app_launcher = None
88
+
89
+ # Initialize PyAutoGUI handler
90
+ pyautogui_handler = None
91
+ if PYAUTOGUI_AVAILABLE:
92
+ try:
93
+ pyautogui_handler = PyAutoGUIToolHandler()
94
+ logger.info("PyAutoGUI handler initialized successfully")
95
+ except Exception as e:
96
+ logger.warning(f"Failed to initialize PyAutoGUI handler: {e}")
97
+ PYAUTOGUI_AVAILABLE = False
98
+
99
+ # Initialize PyWinAuto handler
100
+ pywinauto_handler = None
101
+ if PYWINAUTO_AVAILABLE:
102
+ try:
103
+ pywinauto_handler = PyWinAutoToolHandler()
104
+ logger.info("PyWinAuto handler initialized successfully")
105
+ except Exception as e:
106
+ logger.warning(f"Failed to initialize PyWinAuto handler: {e}")
107
+ PYWINAUTO_AVAILABLE = False
108
+
109
+ # Current attached process
110
+ current_process = None
111
+
112
+ # Cheat Engine bridge (Windows only)
113
+ ce_bridge = None
114
+ if CE_ENGINE_AVAILABLE:
115
+ try:
116
+ ce_bridge = CheatEngineBridge()
117
+ logger.info("Cheat Engine bridge initialized")
118
+ except Exception as e:
119
+ logger.warning(f"Failed to initialize Cheat Engine bridge: {e}")
120
+ CE_ENGINE_AVAILABLE = False
121
+
122
+ # ==========================================
123
+ # PROCESS MANAGEMENT TOOLS
124
+ # ==========================================
125
+
126
+ @mcp.tool()
127
+ def list_processes() -> Dict[str, Any]:
128
+ """List all running processes on the system"""
129
+ try:
130
+ processes = process_manager.list_processes()
131
+ return {
132
+ "success": True,
133
+ "processes": processes,
134
+ "count": len(processes)
135
+ }
136
+ except Exception as e:
137
+ return {
138
+ "success": False,
139
+ "error": str(e)
140
+ }
141
+
142
+ @mcp.tool()
143
+ def attach_to_process(process_id: int) -> Dict[str, Any]:
144
+ """Attach to a specific process by PID"""
145
+ global current_process
146
+ try:
147
+ if not process_whitelist.is_allowed(process_id):
148
+ return {
149
+ "success": False,
150
+ "error": f"Process {process_id} is not in the whitelist"
151
+ }
152
+
153
+ current_process = process_manager.attach_process(process_id)
154
+ return {
155
+ "success": True,
156
+ "process": current_process,
157
+ "message": f"Successfully attached to process {process_id}"
158
+ }
159
+ except Exception as e:
160
+ return {
161
+ "success": False,
162
+ "error": str(e)
163
+ }
164
+
165
+ @mcp.tool()
166
+ def detach_from_process() -> Dict[str, Any]:
167
+ """Detach from the current process"""
168
+ global current_process
169
+ try:
170
+ if current_process:
171
+ process_manager.detach_process()
172
+ current_process = None
173
+ return {
174
+ "success": True,
175
+ "message": "Successfully detached from process"
176
+ }
177
+ return {
178
+ "success": True,
179
+ "message": "No process was attached"
180
+ }
181
+ except Exception as e:
182
+ return {
183
+ "success": False,
184
+ "error": str(e)
185
+ }
186
+
187
+ @mcp.tool()
188
+ def get_process_info(process_id: int = None) -> Dict[str, Any]:
189
+ """Get detailed information about a process"""
190
+ try:
191
+ if process_id:
192
+ info = process_manager.get_process_info(process_id)
193
+ elif current_process:
194
+ info = current_process
195
+ else:
196
+ return {
197
+ "success": False,
198
+ "error": "No process specified and no process currently attached"
199
+ }
200
+ return {
201
+ "success": True,
202
+ "info": info
203
+ }
204
+ except Exception as e:
205
+ return {
206
+ "success": False,
207
+ "error": str(e)
208
+ }
209
+
210
+ # ==========================================
211
+ # MEMORY OPERATIONS TOOLS
212
+ # ==========================================
213
+
214
+ @mcp.tool()
215
+ def read_memory_region(address: str, size: int, data_type: str = "bytes") -> Dict[str, Any]:
216
+ """Read memory from a specific address"""
217
+ try:
218
+ addr = validate_address(address)
219
+ sz = validate_size(size)
220
+
221
+ # This is a placeholder - actual memory reading requires process attachment
222
+ # In a real implementation, this would read from the attached process
223
+ return {
224
+ "success": True,
225
+ "address": hex(addr),
226
+ "size": sz,
227
+ "data_type": data_type,
228
+ "data": f"Memory read from {hex(addr)} (size: {sz}, type: {data_type})",
229
+ "note": "This is a placeholder implementation"
230
+ }
231
+ except Exception as e:
232
+ return {
233
+ "success": False,
234
+ "error": str(e)
235
+ }
236
+
237
+ @mcp.tool()
238
+ def get_memory_regions() -> Dict[str, Any]:
239
+ """Get memory regions of the attached process"""
240
+ try:
241
+ if not current_process:
242
+ return {
243
+ "success": False,
244
+ "error": "No process attached"
245
+ }
246
+
247
+ # Placeholder - actual implementation would enumerate memory regions
248
+ return {
249
+ "success": True,
250
+ "regions": [
251
+ {
252
+ "base_address": "0x140000000",
253
+ "size": 4096,
254
+ "protection": "PAGE_EXECUTE_READ"
255
+ }
256
+ ],
257
+ "note": "This is a placeholder implementation"
258
+ }
259
+ except Exception as e:
260
+ return {
261
+ "success": False,
262
+ "error": str(e)
263
+ }
264
+
265
+ @mcp.tool()
266
+ def scan_memory(pattern: str, start_address: str = None, end_address: str = None) -> Dict[str, Any]:
267
+ """Scan memory for a specific pattern"""
268
+ try:
269
+ # Placeholder - actual implementation would perform memory scan
270
+ return {
271
+ "success": True,
272
+ "pattern": pattern,
273
+ "results": [],
274
+ "note": "This is a placeholder implementation"
275
+ }
276
+ except Exception as e:
277
+ return {
278
+ "success": False,
279
+ "error": str(e)
280
+ }
281
+
282
+ # ==========================================
283
+ # ANALYSIS TOOLS
284
+ # ==========================================
285
+
286
+ @mcp.tool()
287
+ def analyze_structure(address: str, size: int) -> Dict[str, Any]:
288
+ """Analyze memory at address for data structures"""
289
+ try:
290
+ addr = validate_address(address)
291
+ sz = validate_size(size)
292
+
293
+ return {
294
+ "success": True,
295
+ "address": hex(addr),
296
+ "size": sz,
297
+ "analysis": "Structure analysis placeholder",
298
+ "note": "This is a placeholder implementation"
299
+ }
300
+ except Exception as e:
301
+ return {
302
+ "success": False,
303
+ "error": str(e)
304
+ }
305
+
306
+ @mcp.tool()
307
+ def disassemble_code(address: str, size: int, architecture: str = "x64") -> Dict[str, Any]:
308
+ """Disassemble code at a specific address"""
309
+ try:
310
+ addr = validate_address(address)
311
+ sz = validate_size(size)
312
+
313
+ return {
314
+ "success": True,
315
+ "address": hex(addr),
316
+ "size": sz,
317
+ "architecture": architecture,
318
+ "disassembly": [],
319
+ "note": "This is a placeholder implementation"
320
+ }
321
+ except Exception as e:
322
+ return {
323
+ "success": False,
324
+ "error": str(e)
325
+ }
326
+
327
+ @mcp.tool()
328
+ def resolve_pointer_chain(base_address: str, offsets: List[int]) -> Dict[str, Any]:
329
+ """Resolve a multi-level pointer chain"""
330
+ try:
331
+ addr = validate_address(base_address)
332
+
333
+ return {
334
+ "success": True,
335
+ "base_address": hex(addr),
336
+ "offsets": offsets,
337
+ "final_address": "0x0",
338
+ "note": "This is a placeholder implementation"
339
+ }
340
+ except Exception as e:
341
+ return {
342
+ "success": False,
343
+ "error": str(e)
344
+ }
345
+
346
+ # ==========================================
347
+ # CHEAT ENGINE SPECIFIC TOOLS (Windows only)
348
+ # ==========================================
349
+
350
+ if CE_ENGINE_AVAILABLE:
351
+ @mcp.tool()
352
+ def import_cheat_table(file_path: str) -> Dict[str, Any]:
353
+ """Import a Cheat Engine table (.CT) file"""
354
+ try:
355
+ if not ce_bridge:
356
+ return {
357
+ "success": False,
358
+ "error": "Cheat Engine bridge not available"
359
+ }
360
+
361
+ parser = CheatTableParser()
362
+ entries = parser.parse_ct_file(file_path)
363
+
364
+ return {
365
+ "success": True,
366
+ "entries": entries,
367
+ "count": len(entries)
368
+ }
369
+ except Exception as e:
370
+ return {
371
+ "success": False,
372
+ "error": str(e)
373
+ }
374
+
375
+ @mcp.tool()
376
+ def execute_lua_script(script_content: str, safe_mode: bool = True) -> Dict[str, Any]:
377
+ """Execute a Lua script (Cheat Engine)"""
378
+ try:
379
+ if not ce_bridge:
380
+ return {
381
+ "success": False,
382
+ "error": "Cheat Engine bridge not available"
383
+ }
384
+
385
+ # Placeholder - actual implementation would execute Lua script
386
+ return {
387
+ "success": True,
388
+ "result": "Lua script executed",
389
+ "note": "This is a placeholder implementation"
390
+ }
391
+ except Exception as e:
392
+ return {
393
+ "success": False,
394
+ "error": str(e)
395
+ }
396
+
397
+ # ==========================================
398
+ # CHEAT TABLE FILESYSTEM TOOLS
399
+ # ==========================================
400
+
401
+ @mcp.tool()
402
+ def read_file(path: str) -> str:
403
+ """Read file contents"""
404
+ path_obj = Path(path)
405
+
406
+ if not path_obj.exists():
407
+ return f"File not found: {path}"
408
+
409
+ if not path_obj.is_file():
410
+ return f"Path is not a file: {path}"
411
+
412
+ try:
413
+ with path_obj.open("r", encoding="utf-8") as f:
414
+ content = f.read()
415
+
416
+ return f"Contents of {path}:\n{content}"
417
+
418
+ except UnicodeDecodeError:
419
+ return f"File is not text or uses unsupported encoding: {path}"
420
+
421
+ @mcp.tool()
422
+ def write_file(path: str, content: str) -> str:
423
+ """Write content to a file"""
424
+ path_obj = Path(path)
425
+
426
+ try:
427
+ path_obj.parent.mkdir(parents=True, exist_ok=True)
428
+ with path_obj.open("w", encoding="utf-8") as f:
429
+ f.write(content)
430
+
431
+ return f"Successfully wrote to {path}"
432
+
433
+ except Exception as e:
434
+ return f"Error writing to {path}: {e}"
435
+
436
+ @mcp.tool()
437
+ def list_directory(path: str = ".") -> str:
438
+ """List directory contents"""
439
+ path_obj = Path(path)
440
+
441
+ if not path_obj.exists():
442
+ return f"Directory not found: {path}"
443
+
444
+ if not path_obj.is_dir():
445
+ return f"Path is not a directory: {path}"
446
+
447
+ try:
448
+ items = list(path_obj.iterdir())
449
+ result = f"Contents of {path}:\n"
450
+ for item in sorted(items):
451
+ prefix = "DIR " if item.is_dir() else "FILE"
452
+ result += f"{prefix}: {item.name}\n"
453
+
454
+ return result
455
+
456
+ except Exception as e:
457
+ return f"Error listing directory {path}: {e}"
458
+
459
+ # ==========================================
460
+ # END CHEAT TABLE FILESYSTEM TOOLS
461
+ # ==========================================
462
+
463
+
464
+ def main():
465
+ """Main server entry point"""
466
+ try:
467
+ # Initialize server configuration
468
+ if args.debug:
469
+ logging.getLogger().setLevel(logging.DEBUG)
470
+ logger.debug("Debug mode enabled")
471
+
472
+ if args.read_only:
473
+ logger.info("Read-only mode enabled")
474
+
475
+ # Load configuration
476
+ server_config.load_config(args.config)
477
+
478
+ # Initialize whitelist
479
+ process_whitelist.load_whitelist(server_config.get_whitelist_path())
480
+
481
+ # Initialize application launcher
482
+ global app_launcher
483
+ app_launcher = ApplicationLauncher(process_whitelist)
484
+
485
+ # Set session file for persistence (optional)
486
+ session_file = os.path.join(os.path.dirname(server_config.get_whitelist_path()), 'launcher_session.json')
487
+ app_launcher.set_session_file(session_file)
488
+ logger.info("Application launcher initialized")
489
+
490
+ # Register automation tools if available
491
+ if AUTOMATION_AVAILABLE:
492
+ try:
493
+ automation_tools = register_automation_tools(mcp)
494
+ logger.info(f"Registered {len(automation_tools)} automation tools")
495
+ except Exception as e:
496
+ logger.error(f"Failed to register automation tools: {e}")
497
+
498
+ logger.info("MCP Cheat Engine Server starting...")
499
+
500
+ # Run the MCP server
501
+ mcp.run()
502
+
503
+ except KeyboardInterrupt:
504
+ logger.info("Server shutdown requested")
505
+ except Exception as e:
506
+ logger.error(f"Server error: {e}")
507
+ sys.exit(1)
508
+ finally:
509
+ # Cleanup
510
+ if current_process:
511
+ try:
512
+ process_manager.detach_process()
513
+ logger.info("Cleaned up process attachment")
514
+ except:
515
+ pass
516
+
517
+
518
+ if __name__ == "__main__":
519
+ main()
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,16 @@
1
+ """
2
+ Process Management Module
3
+ Handles process enumeration, attachment, and monitoring
4
+ """
5
+
6
+ from .manager import ProcessManager, ProcessInfo
7
+ from .permissions import PermissionChecker
8
+ from .monitors import ProcessMonitor, ProcessState
9
+
10
+ __all__ = [
11
+ 'ProcessManager',
12
+ 'ProcessInfo',
13
+ 'PermissionChecker',
14
+ 'ProcessMonitor',
15
+ 'ProcessState'
16
+ ]