A Model Context Protocol (MCP) server for autonomous C64 debugging via the VICE emulator.
vice-mcp bridges AI agents to the VICE Commodore 64 emulator, enabling autonomous debugging of 6502 assembly programs. Unlike raw protocol wrappers, it provides a semantic layer that interprets C64-specific data structures and returns meaningful, actionable information.
Why this exists:
- AI agents need more than hex dumps—they need interpreted data with context
- Debugging C64 code requires understanding VIC-II banks, PETSCII encoding, sprite pointers, and memory layouts
- Every response includes hints suggesting next steps and related tools
Key differentiators:
- Semantic output:
readScreenreturns text, not screen codes.readVicStateexplains graphics modes, not register bits. - Actionable hints: Every response suggests what to do next
- Cross-references: Tools point to related tools for common workflows
- Agent-friendly errors: Clear error codes and recovery suggestions
- Node.js 18 or later
- VICE emulator with binary monitor enabled
# x64sc is the accurate C64 emulator (recommended)
x64sc -binarymonitor -binarymonitoraddress ip4://127.0.0.1:6502
# Or with x64 (faster, less accurate)
x64 -binarymonitor -binarymonitoraddress ip4://127.0.0.1:6502The binary monitor listens on port 6502 by default.
npx @simen/vice-mcpnpx github:simen/vice-mcpgit clone https://github.com/simen/vice-mcp.git
cd vice-mcp
npm install
npm run build
npm startThe quickest way to get started with Claude Code:
1. Start VICE with binary monitor:
x64sc -binarymonitor -binarymonitoraddress ip4://127.0.0.1:65022. Add the MCP server:
claude mcp add vice-mcp -- npx github:simen/vice-mcp3. Restart Claude Code to load the new MCP server.
That's it! You can now ask Claude Code to debug your C64 programs.
Alternatively, add to ~/.claude/claude_desktop_config.json:
{
"mcpServers": {
"vice-mcp": {
"command": "npx",
"args": ["github:simen/vice-mcp"]
}
}
}Add to your MCP client configuration (e.g., Claude Desktop, Cursor, or custom agent):
{
"mcpServers": {
"vice": {
"command": "npx",
"args": ["@simen/vice-mcp"]
}
}
}Or for local development:
{
"mcpServers": {
"vice": {
"command": "node",
"args": ["/path/to/vice-mcp/dist/index.js"]
}
}
}| Tool | Description |
|---|---|
connect |
Connect to VICE (default: 127.0.0.1:6502) |
disconnect |
Disconnect from VICE |
status |
Get connection state and emulation status |
| Tool | Description |
|---|---|
readMemory |
Read raw bytes with hex dump and ASCII |
writeMemory |
Write bytes to memory |
| Tool | Description |
|---|---|
getRegisters |
Get A, X, Y, SP, PC, and flags (interpreted) |
step |
Single-step execution (with step-over option) |
continue |
Resume execution |
reset |
Soft or hard reset |
runTo |
Run until specific address (temporary breakpoint) |
disassemble |
Disassemble 6502 code with KERNAL labels |
| Tool | Description |
|---|---|
setBreakpoint |
Set execution breakpoint |
deleteBreakpoint |
Remove breakpoint or watchpoint |
listBreakpoints |
List all breakpoints |
toggleBreakpoint |
Enable/disable breakpoint |
setWatchpoint |
Set memory read/write watchpoint |
listWatchpoints |
List all watchpoints |
| Tool | Description |
|---|---|
readScreen |
Get screen as text (PETSCII decoded) with summary mode |
readColorRam |
Get color RAM with color names and usage stats |
readVicState |
Full VIC-II state: graphics mode, colors, banks, sprites |
readSprites |
All 8 sprites: position, visibility, colors, pointers |
| Tool | Description |
|---|---|
screenshot |
Capture display buffer with palette |
renderScreen |
ASCII art rendering of display |
| Tool | Description |
|---|---|
saveSnapshot |
Save complete machine state to file |
loadSnapshot |
Load machine state from file |
loadProgram |
Load and optionally run PRG/D64/T64 files |
1. connect() → Establish connection
2. loadProgram("game.prg") → Load the program
3. setBreakpoint(0x0810) → Break at main loop
4. continue() → Run until breakpoint
5. getRegisters() → Check CPU state
6. readScreen() → See what's on screen
7. step(count: 5) → Execute 5 instructions
8. disassemble() → See code at current PC
1. readVicState() → Check sprite enable bits
2. readSprites(enabledOnly: true) → Get enabled sprite details
→ Response includes visibility check and position analysis
3. If sprite not visible, hint tells you why (off-screen, wrong bank, etc.)
1. setWatchpoint(startAddress: 0x0400, type: "store")
→ Watch for writes to screen RAM
2. continue()
→ Execution stops when something writes to screen
3. getRegisters()
→ See PC to find the code that wrote
4. disassemble()
→ Understand what the code is doing
1. saveSnapshot("before-test.vsf") → Save state
2. [Make changes, test things]
3. loadSnapshot("before-test.vsf") → Restore to known state
All responses include:
- Structured data with
valueandhexrepresentations _metablock with connection statehintfield with contextual next steps
Example getRegisters response:
{
"a": { "value": 65, "hex": "$41" },
"x": { "value": 0, "hex": "$00" },
"y": { "value": 0, "hex": "$00" },
"sp": { "value": 243, "hex": "$f3", "stackTop": "$01f3" },
"pc": { "value": 2049, "hex": "$0801" },
"flags": {
"negative": false,
"overflow": false,
"zero": false,
"carry": false,
"string": "nv-bdizc"
},
"hint": "CPU state looks normal",
"_meta": {
"connected": true,
"running": false,
"host": "127.0.0.1",
"port": 6502
}
}┌─────────────────────────────────────────────────────────┐
│ MCP Client (Agent) │
└─────────────────────────────────────────────────────────┘
│
│ MCP Protocol (stdio)
▼
┌─────────────────────────────────────────────────────────┐
│ src/index.ts │
│ (MCP Server) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Tool Handlers (24 tools) │ │
│ │ • Connection: connect, disconnect, status │ │
│ │ • Memory: readMemory, writeMemory │ │
│ │ • CPU: getRegisters, step, continue, reset │ │
│ │ • Breakpoints: set, delete, list, toggle │ │
│ │ • Watchpoints: set, list │ │
│ │ • Semantic: readScreen, readVicState, etc. │ │
│ │ • Visual: screenshot, renderScreen │ │
│ │ • State: saveSnapshot, loadSnapshot, loadPrg │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
│ Uses
▼
┌─────────────────────────────────────────────────────────┐
│ src/protocol/client.ts │
│ (ViceClient) │
│ • TCP socket connection to VICE │
│ • Binary protocol encoding/decoding │
│ • Request/response correlation │
│ • Checkpoint (breakpoint/watchpoint) tracking │
└─────────────────────────────────────────────────────────┘
│
│ TCP Socket
▼
┌─────────────────────────────────────────────────────────┐
│ VICE Binary Monitor │
│ (Port 6502) │
└─────────────────────────────────────────────────────────┘
| File | Purpose |
|---|---|
src/index.ts |
MCP server, tool definitions, semantic layer |
src/protocol/client.ts |
VICE binary monitor client |
src/protocol/types.ts |
Protocol constants and types |
src/utils/c64.ts |
C64 utilities (PETSCII, colors, VIC banks) |
src/utils/disasm.ts |
6502 disassembler with all addressing modes |
- Semantic over raw: Return interpreted data, not just bytes
- Hints everywhere: Every response suggests next actions
- Cross-references: Tools reference related tools
- Fail informatively: Errors explain what went wrong and how to fix it
- Agent-first: Designed for autonomous operation, not human CLI use
vice-mcp implements the VICE Binary Monitor Protocol. Key commands used:
| Code | Command | Purpose |
|---|---|---|
| 0x01 | MemoryGet | Read memory |
| 0x02 | MemorySet | Write memory |
| 0x12 | CheckpointSet | Create breakpoint/watchpoint |
| 0x13 | CheckpointDelete | Remove checkpoint |
| 0x15 | CheckpointToggle | Enable/disable checkpoint |
| 0x31 | RegistersGet | Read CPU registers |
| 0x41 | Dump | Save snapshot |
| 0x42 | Undump | Load snapshot |
| 0x81 | Continue | Resume execution |
| 0x82 | Step | Single-step |
| 0x84 | DisplayGet | Capture screen |
| 0x91 | PaletteGet | Get color palette |
| 0xdd | AutoStart | Load and run program |
MIT