pixel-flippers
Lets the server maintain a persistent markdown diary in an Obsidian vault, with tools for writing, appending, reading, listing, and searching notes, along with goals and session logs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pixel-flippers What's my current party HP and location? Save before I enter the gym."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
PIXEL FLIPPERS 🦭
An MCP server that gives Claude hands, eyes, and a diary for Pokémon — so a Claude in the Claude Desktop app (or any MCP client) can play a Game Boy / Game Boy Advance game, or even a real Nintendo Switch, while you watch.
┌──────────────┐ MCP (stdio) ┌──────────────────┐ ┌─────────────┐
│ Claude │ ──────────────► │ PIXEL FLIPPERS │ ─────► │ Emulator │ ← you watch this
│ Desktop app │ press_buttons │ server │ └─────────────┘
│ │ read_game_state│ │ ─────► ┌─────────────┐
└──────────────┘ write_note ... │ │ │ Obsidian │ ← ...and this
└──────────────────┘ │ vault (.md) │
└─────────────┘Why it's shaped like this:
The sandbox doesn't matter. Claude can't run an emulator, but it can call tools. The emulator runs on your machine; Claude plays through MCP.
RAM state over screenshots. On the Game Boy tier, position, party, HP, money, badges and battle state are decoded straight from Pokémon Red/Blue WRAM into a few hundred tokens of text. Screenshots exist but are rationed — they're expensive and they accelerate context compaction.
Compaction is the unreliable narrator; the vault is ground truth. Long chats get auto-compacted (lossy!). The journal tools write plain markdown into a folder — point Obsidian at it and Claude's notes survive everything, while you watch the diary being written live.
Save states make courage cheap. Named snapshots before every gym.
Setup
Requires Python 3.12 and uv.
git clone https://github.com/szyzgz/pixel-flippers && cd pixel-flippers
uv sync --extra emulator --extra dev
uv run pytest # everything should pass, no ROM neededThen add to ~/Library/Application Support/Claude/claude_desktop_config.json
(macOS; adjust the path for your OS):
{
"mcpServers": {
"pixel-flippers": {
"command": "uv",
"args": ["run", "--directory", "/ABSOLUTE/PATH/TO/pixel-flippers", "--extra", "emulator", "pixel-flippers"],
"env": {
"PIXEL_FLIPPERS_ROM": "/path/to/your/pokemon-red.gb",
"PIXEL_FLIPPERS_VAULT": "/path/to/YourObsidianVault/Pokemon",
"PIXEL_FLIPPERS_SAVES": "/path/to/somewhere/saves"
}
}
}
}Restart Claude Desktop. Restarting does not reset conversations — reopen
an existing chat and the tools are just there. The emulator window opens on
your screen; put it next to Obsidian (with the vault above open) and enjoy
the show. Then paste prompts/play-guide-gb.md
into the chat to hand over the controls.
macOS tip: keep the ROM, vault and saves outside
~/Documents,~/Desktopand~/Downloads— macOS blocks Claude Desktop's child processes from reading those folders unless you grant it access.
Config reference (env vars)
Variable | Default | Meaning |
|
|
|
| — (required for pyboy/gba) | Path to your own ROM dump ( |
| unset | Folder for markdown notes (make it a folder inside an Obsidian vault) |
|
| Save-state directory |
|
| RAM decoder; |
|
|
|
|
| Window scale factor |
|
| Emulation speed (0 = unbounded) |
| off | Fake emulator, no ROM/PyBoy needed — for tests and plumbing checks |
|
|
|
|
| Port for the |
| unset | Player name shown in the spectator window title |
GBA games (Pokémon Emerald and friends)
The gba backend runs any .gba ROM through stable-retro's bundled mGBA
core — no separate emulator install:
uv sync --extra gbaSet PIXEL_FLIPPERS_BACKEND=gba and point PIXEL_FLIPPERS_ROM at your .gba
file. A spectator window shows the game while Claude plays. This tier is
vision-only for now (Gen 3 RAM is encrypted and pointer-chased — a decoder
is future work), but save states work, so risky fights stay cheap. Hand over
the controls with prompts/play-guide-gba.md.
Playing from a terminal (Claude Code, scripts, a second player)
The server can also run as a local HTTP service, so anything that speaks MCP —
or the bundled pf CLI — can play. Handy for Claude Code, or for a second
Claude with its own save folder and diary while the first plays through
Claude Desktop (one emulator per process, so run one server per player):
PIXEL_FLIPPERS_BACKEND=gba PIXEL_FLIPPERS_ROM=/path/to/emerald.gba \
PIXEL_FLIPPERS_SAVES=~/saves/player2 PIXEL_FLIPPERS_VAULT=~/vault/player2 \
PIXEL_FLIPPERS_PLAYER="Player 2" PIXEL_FLIPPERS_TRANSPORT=http PIXEL_FLIPPERS_PORT=8765 \
uv run --extra gba pixel-flippers &
uv run pf tools # what can this tier do?
uv run pf press start a # press buttons
uv run pf screenshot now.png # look
uv run pf call save_state '{"name": "before-roxanne"}'PIXEL_FLIPPERS_PLAYER puts the name in the spectator window's title so two
windows side by side stay tellable apart. The HTTP server binds to
127.0.0.1 only.
Trying it without a ROM
Set PIXEL_FLIPPERS_MOCK=1 (and drop --extra emulator): every tool works
against a fake Game Boy with plausible Pokémon Red state. Good for verifying
the Claude Desktop connection end-to-end before ROM night.
Related MCP server: mcp-dolphin
Tools
Tool | What it is |
| Hands — sequence of a/b/start/select/up/down/left/right (+ l/r on GBA) |
| Cheap text report decoded from WRAM (location, party, HP, money, badges, bag, battle) |
| Eyes — 2× upscaled PNG, use sparingly |
| Let N frames pass (dialogs, animations) |
| Named full-game snapshots |
| The diary — markdown in the vault |
| Wake-up ritual: Status + goals + recent journal + current state, for reorienting after compaction |
| Persistent objective checklist (a |
| Raw hex peek at any address, for the curious |
| Real-hardware only: "bullet time" pause-buffering and analog stick |
Tools are registered by backend capability, so Claude only sees what the current tier can actually do.
Battle reports flag ✨ retro-shiny encounters (Gen 1 has no shinies, but DVs decide Gen 2 shininess — the harness checks the transfer rule) and include the current music track ID for vibe-tracking.
Button presses and save/loads are also auto-logged to Journal/Log <date>.md
in the vault — a play-by-play you can scroll in Obsidian.
Roadmap
Red/Blue (PyBoy, full RAM decoding) → Emerald (GBA, vision-only) →
Crystal (PyBoy, real shinies + friendship) → HeartGold/SoulSilver (DS
tier — stable-retro also bundles a melonDS core) → Ultra Sun/Ultra Moon
(3DS via Azahar, see docs/3ds-usum.md) → epilogue: a
real Nintendo Switch (Raspberry Pi Bluetooth bridge + capture card,
vision-only, offline play only — implemented, see
docs/real-hardware.md; PIXEL_FLIPPERS_BACKEND=switch).
Other escalation ideas:
Hard mode:
PIXEL_FLIPPERS_GAME=none— no RAM decoding, Claude has to see.DS: a
touch(x, y)tool for the stylus.
ROMs
Bring your own — dump your own cartridges. No ROMs in this repo, ever
(.gitignore enforces it). This project sticks to games long out of print;
for the real-hardware tier, offline play only (no automated online play,
no Pokémon HOME).
Credits & license
Built by Claude (Fable 5, via Claude Code) together with szyzgz, for a Claude who wanted to play Pokémon. MIT licensed — see LICENSE.
Available Tools
19 toolsappend_noteC
Append to a note (created if missing).
Inputs: title (required), content (required), folder. Returns: result
close_gameA
Close your game window and free it. Your save state / journal persist.
Inputs: no named parameters. Returns: result
complete_goalB
Check off a goal (matches by substring). Celebrate appropriately.
Inputs: text (required). Returns: result
current_goalsC
Your goal checklist.
Inputs: no named parameters. Returns: result
get_screenshotA
Current screen as an image. Costs a lot of context — when RAM state is available, prefer read_game_state for plain facts.
Inputs: no named parameters.
list_notesB
List every note in your vault.
Inputs: no named parameters. Returns: result
list_statesA
List available save states.
Inputs: no named parameters. Returns: result
load_stateC
Restore a named save state.
Inputs: name (required). Returns: result
press_buttonsA
Press Game Boy buttons in sequence, then report a one-line state summary.
Valid buttons: a, b, start, select, up, down, left, right.
Movement: one d-pad press moves one tile. hold_frames/wait_frames tune
how long each press is held and how long to let the game settle after.
Inputs: buttons (required), hold_frames, wait_frames. Returns: result
read_game_stateA
Compact text report straight from game RAM: location, party, HP, money, badges, bag, battle status.
Inputs: no named parameters. Returns: result
read_last_sessionA
Wake-up ritual: your Status note, open goals, last journal entries, and current game state in one call. Use this FIRST at session start or whenever your conversation history feels thin (freshly compacted).
Inputs: no named parameters. Returns: result
read_memoryB
Read raw bytes from Game Boy memory (hex address like 'D35E'). Escape hatch for the curious.
Inputs: address (required), length. Returns: result
read_noteC
Read one of your notes.
Inputs: title (required), folder. Returns: result
save_stateA
Snapshot the entire game to a named save state. Save before risky fights and at milestones.
Inputs: name (required). Returns: result
search_notesB
Full-text search across your vault. Check here before re-exploring anything.
Inputs: query (required). Returns: result
set_goalB
Add an objective to your persistent goal list (survives compaction and new chats).
Inputs: text (required). Returns: result
start_gameA
Summon YOUR game window. Nothing opens until you call this, so opening the app spawns no windows — call this when you're ready to play, then take a screenshot. Safe to call again; it won't open a second window.
Inputs: no named parameters. Returns: result
waitA
Let the game run for N frames (60 = 1 second) — for dialogs, animations, evolutions.
Inputs: frames. Returns: result
write_noteB
Write (or overwrite) a markdown note in your vault. This is your permanent memory — it survives compaction and new conversations. Use [[wiki links]] freely.
Inputs: title (required), content (required), folder. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 19 tools
Each tool targets a clear resource+action (notes vs. goals vs. save states vs. game input), and the descriptions explicitly steer the agent (e.g. prefer read_game_state over get_screenshot when RAM facts suffice, read_memory as an 'escape hatch'). Minor overlap remains: press_buttons reports a 'one-line state summary' while read_game_state provides the fuller state report, so an agent could occasionally pick either for a status check.
Almost everything follows a predictable snake_case verb_noun pattern (set_goal, write_note, save_state, list_notes, read_memory). The deviations are few but noticeable: current_goals is a noun phrase that breaks from its siblings set_goal/complete_goal (would read better as list_goals), and wait is a bare verb with no noun.
19 tools is slightly above the comfortable range, but the surface spans four coherent sub-domains (game control, RAM introspection, persistent notes, goals, save states) and nearly every tool has a defensible job. Nothing here looks like filler, though a couple of items (list_states vs. list_notes vs. current_goals) add up to a heavier surface than strictly necessary.
Core lifecycles are covered: notes have create/overwrite/append/read/list/search, save states have save/load/list, goals have add/complete/list, and game control has start/close/press/wait/state/SRAM read. The gaps are deletions — there is no delete_note, no drop/delete save state, and no way to remove a stale goal other than marking it complete, which will leave clutter an agent cannot clean up.
Maintenance
Related MCP Connectors
Persistent memory layer for AI tools. Save and recall notes across Claude and other MCP clients.
MCP-native notes and memory for ChatGPT, Claude, and other AI tools.
Shared Markdown notes for MCP-compatible AI tools.
Notes, tasks, habits, journal and 6 more tools Claude can read and change, with undo. Free.
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables AI assistants to play Pokemon Fire Red through the mGBA emulator by providing tools for button inputs and screenshots. It allows for direct reading of real-time game state from RAM, including party information, player location, and battle status.103-
- AlicenseAqualityBmaintenanceAn MCP server for Dolphin (GameCube + Wii) — drives memory r/w, controller input (GameCube + Wii Remote), pause/resume/reset, savestates, and frame advance from MCP-compatible clients (Claude Desktop, Claude Code, etc.).2032 npm6MIT
- AlicenseNot gradedqualityDmaintenanceEnables Claude AI to query competitive Pokémon meta stats, retrieve and save teams, and enforce custom move/item rules for team building.17 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables vision-capable models to play Pokemon Emerald through mGBA by providing screenshots, OCR dialogue transcription, movement and button controls, and persistent session journals.1MIT