Loading...
Loading...
Connect AI agents to Cheat Engine for automated memory analysis, reverse engineering, and debugging via MCP
npx skill4agent add aradotso/mcp-skills cheatengine-mcp-bridgeSkill by ara.so — MCP Skills collection.
[[base+0x10]+0x20]+0x8AI Agent (Claude/Cursor)
↕ MCP Protocol (JSON-RPC over stdio)
Python MCP Server (mcp_cheatengine.py)
↕ Named Pipe (async)
Cheat Engine Lua Bridge (ce_mcp_bridge.lua)
↕ CE API
Target Process Memorypywin32cd MCP_Server
pip install -r requirements.txtpip install mcp pywin32FileExecute ScriptMCP_Server/ce_mcp_bridge.luaExecuteTableShow Cheat Table Lua Scriptdofile([[C:\path\to\cheatengine-mcp-bridge\MCP_Server\ce_mcp_bridge.lua]])[MCP v12.0.0] MCP Server Listening on: CE_MCP_Bridge_v99~/.config/claude/claude_desktop_config.json{
"mcpServers": {
"cheatengine": {
"command": "python",
"args": ["C:/path/to/cheatengine-mcp-bridge/MCP_Server/mcp_cheatengine.py"]
}
}
}.cursorrules{
"mcp": {
"servers": {
"cheatengine": {
"command": "python",
"args": ["C:/path/to/cheatengine-mcp-bridge/MCP_Server/mcp_cheatengine.py"]
}
}
}
}~/.codex/config.toml[mcp_servers.cheatengine]
command = "python"
args = ['C:\path\to\cheatengine-mcp-bridge\MCP_Server\mcp_cheatengine.py']User: "Attach to notepad.exe"
Agent uses: open_process
Args: {"process_name": "notepad.exe"}Tool: get_process_list
Returns: [{"pid": 1234, "name": "game.exe"}, ...]Tool: create_process
Args: {"path": "C:\\Games\\game.exe"}Tool: get_process_info
Returns: {"pid": 5678, "name": "game.exe", "base_address": "0x400000"}Tool: read_integer
Args: {"address": "0x12345678"}
Returns: {"value": 15000}Tool: read_float
Args: {"address": "game.exe+0x1234", "is_double": false}
Returns: {"value": 100.5}Tool: read_string
Args: {"address": "0x400000", "length": 64}
Returns: {"value": "PlayerName"}Tool: read_pointer_chain
Args: {"base": "game.exe+0x1000", "offsets": [0x10, 0x20, 0x8]}
Returns: {"final_address": "0x789ABC", "value": 42}Tool: read_memory
Args: {"address": "0x400000", "size": 16}
Returns: {"hex": "4D5A90000300000004000000FFFF0000", "bytes": [77, 90, ...]}Tool: scan_all
Args: {
"value_type": "4byte",
"scan_type": "exact",
"value": "15000",
"writable": true,
"executable": false
}
Returns: {"count": 47, "addresses": ["0x123000", "0x456000", ...]}Tool: next_scan
Args: {"value": "15100"}
Returns: {"count": 3, "addresses": ["0x123000", ...]}Tool: aob_scan
Args: {
"pattern": "48 8B 05 ?? ?? ?? ?? 48 85 C0",
"writable": false,
"executable": true
}
Returns: {"addresses": ["0x401000", "0x402500"]}Tool: pointer_scan
Args: {
"address": "0x789000",
"max_level": 5,
"max_offset": 4096
}
Returns: {"count": 12, "results": [{"base": "game.exe+0x1000", "offsets": [0x10, 0x8]}]}Tool: disassemble
Args: {"address": "0x401000", "count": 10}
Returns: {
"instructions": [
{"address": "0x401000", "bytes": "55", "disassembly": "push rbp"},
{"address": "0x401001", "bytes": "4889E5", "disassembly": "mov rbp,rsp"}
]
}Tool: analyze_function
Args: {"address": "0x401000"}
Returns: {
"prologue": "push rbp; mov rbp,rsp",
"calls_count": 3,
"references": ["0x402000", "0x403000"]
}Tool: get_rtti_classname
Args: {"address": "0x500000"}
Returns: {"classname": "CPlayerInventory"}Tool: find_references
Args: {"address": "0x600000"}
Returns: {"count": 5, "references": ["0x401234", "0x402567"]}Tool: dissect_structure
Args: {"address": "0x500000", "size": 256}
Returns: {
"fields": [
{"offset": "0x00", "type": "vtable", "value": "0x401000"},
{"offset": "0x08", "type": "int32", "value": 15},
{"offset": "0x10", "type": "pointer", "value": "0x600000"}
]
}Tool: set_breakpoint
Args: {
"address": "0x401000",
"type": "hardware",
"condition": "rax==5"
}Tool: set_data_breakpoint
Args: {"address": "0x789000", "size": 4, "type": "write"}Tool: start_dbvm_watch
Args: {"address": "0x401000"}Tool: what_writes
Args: {"address": "0x789000"}Tool: what_accesses
Args: {"address": "0x789000", "type": "read"}Tool: write_integer
Args: {"address": "0x123000", "value": 9999, "size": 4}Tool: write_memory
Args: {"address": "0x401000", "bytes": [0x90, 0x90, 0x90]}Tool: freeze_address
Args: {"address": "0x123000", "value": 1000, "description": "Player Health"}Tool: inject_dll
Args: {"dll_path": "C:\\mods\\trainer.dll"}Tool: execute_code
Args: {
"code": "mov rax, 1; ret",
"address": "0x500000"
}Tool: auto_assemble
Args: {
"script": "[ENABLE]\nalloc(hook,128)\nhook:\n mov [health],#999\n ret"
}Tool: register_symbol
Args: {"name": "PlayerHealth", "address": "0x789000"}Tool: get_symbol_info
Args: {"symbol": "PlayerHealth"}
Returns: {"address": "0x789000", "type": "int32"}Tool: enable_windows_symbols
Args: {"enable": true}Tool: load_table
Args: {"path": "C:\\cheats\\game.CT"}Tool: save_table
Args: {"path": "C:\\cheats\\backup.CT"}Tool: get_address_list
Returns: [
{"description": "Health", "address": "0x789000", "value": 100},
{"description": "Gold", "address": "0x789100", "value": 5000}
]User: "Find my gold amount, it's currently 15000"
Agent workflow:
1. scan_all(value_type="4byte", value="15000")
→ Returns 47 addresses
User: "I bought something, gold is now 14750"
2. next_scan(value="14750")
→ Filters to 3 addresses
User: "What writes to the first one?"
3. set_data_breakpoint(address=results[0], type="write")
4. [User triggers gold change in game]
5. get_debug_info()
→ Returns instruction that modified gold
User: "Disassemble that function"
6. disassemble(address=breakpoint_address, count=50)
→ Shows full AddGold/SubtractGold logicUser: "Find the player's coordinates"
Agent workflow:
1. scan_all(value_type="float", value="125.5") # Current X position
2. next_scan(value="126.3") # After moving
3. pointer_scan(address=result_address, max_level=5)
→ Finds: [[game.exe+0x1234]+0x18]+0x30
User: "Verify that pointer is stable"
4. read_pointer_chain(base="game.exe+0x1234", offsets=[0x18, 0x30])
5. register_symbol(name="PlayerX", address=final_address)User: "I found the health function at 0x401000, make it update-resistant"
Agent workflow:
1. disassemble(address="0x401000", count=20)
2. analyze_function(address="0x401000")
3. Identify unique byte pattern with wildcards:
"48 8B 05 ?? ?? ?? ?? 48 85 C0 74 ?? 8B 40 ??"
4. aob_scan(pattern=generated_pattern)
→ Verify only 1 result
5. Returns AOB for use in trainer scriptsUser: "What's at address 0x500000?"
Agent workflow:
1. get_rtti_classname(address="0x500000")
→ "CPlayerInventory"
2. dissect_structure(address="0x500000", size=256)
→ 0x00: vtable
→ 0x08: itemCount (int32) = 15
→ 0x10: itemArray (pointer) = 0x600000
3. read_pointer_chain(base="0x500000", offsets=[0x10])
4. dissect_structure(address="0x600000", size=64)
→ Array of CItem objects# Timeout for MCP tool calls (default: 30 seconds)
set CE_MCP_TIMEOUT=60
# Enable shell execution tools (DANGEROUS - arbitrary code execution)
set CE_MCP_ALLOW_SHELL=1ce_mcp_bridge.lua-- Change named pipe (if multiple instances needed)
local PIPE_NAME = "\\\\.\\pipe\\CE_MCP_Bridge_v99"
-- Adjust worker thread wait time
local WORKER_WAIT_MS = 10
-- Enable verbose logging
DEBUG_MODE = truedofile()dofile([[C:\path\to\ce_mcp_bridge.lua]])MCP Server Listening on: CE_MCP_Bridge_v99pip install mcp pywin32ping{"success": true, "version": "12.0.0"}Tool: get_process_list
→ Find exact process name
Tool: open_process
Args: {"process_name": "exact_name.exe"} # Case-sensitive, include .exeTool: pointer_scan
Args: {
"address": "0x789000",
"max_level": 7, # Increase depth
"max_offset": 8192 # Increase offset range
}# Too generic:
"89 45 ??"
# More specific (add surrounding instructions):
"48 8B 05 ?? ?? ?? ?? 48 85 C0 74 ?? 89 45 ?? C3"Tool: aob_scan
Args: {
"pattern": "...",
"writable": false,
"executable": true, # Only search code sections
"start_address": "game.exe",
"end_address": "game.exe+0x500000"
}cd MCP_Server
python test_mcp.py✅ Memory Reading: 6/6 tests passed
✅ Process Info: 4/4 tests passed
✅ Code Analysis: 8/8 tests passed
✅ Breakpoints: 4/4 tests passed
✅ DBVM Functions: 3/3 tests passed
✅ Utility Commands: 11/11 tests passed
────────────────────────────────────
Total: 36/37 PASSED (100% success)User: "Create a health trainer for game.exe"
Agent executes:
1. open_process(process_name="game.exe")
2. scan_all(value_type="4byte", value="100") # Current health
3. [User takes damage]
4. next_scan(value="85")
5. [Repeat until 1-3 addresses remain]
6. set_data_breakpoint(address=result[0], type="write")
7. [User takes damage again]
8. disassemble(address=breakpoint_hit, count=30)
9. Identify health variable and function
10. generate_api_hook_script(address=health_function)
11. auto_assemble(script=hook_script)
12. freeze_address(address=health_addr, value=999, description="Infinite Health")
13. save_table(path="C:\\trainers\\game_trainer.CT")
Result: Cheat table with working infinite health