36 tools · JSON-RPC 2.0 · macOS

An AI assistant that can
actually see your game.

FRED-80 has an MCP (Model Context Protocol) server built into the engine itself. Point Claude, Gemini, or any MCP-capable assistant at a running FRED-80 process and it can read your sprites, watch for Lua errors, press buttons in your live cart, and edit code — without you copy-pasting a single screenshot.

JSON-RPC / TCP Bearer-token auth Local only 8 tool categories

Why this matters.

Normally, getting an AI assistant to help with a game means describing what's on screen, pasting error messages by hand, and never being quite sure the assistant is looking at the same state you are. FRED-80's MCP server closes that loop: the assistant can query the engine directly and act on it — the same running process you're looking at, not a description of it.

Three things to wire up.

FRED-80 speaks MCP over a local TCP socket. Most AI hosts only know how to spawn a process over stdio, so a small bridge script sits in between.

01

Launch FRED-80 with a token

$ export FRED80_MCP_TOKEN=some-long-random-string
$ ./fred-80

The MCP server listens on 127.0.0.1:8090 and only accepts connections that present this token. Currently macOS only — the tool code compiles everywhere, but the live socket server itself is stubbed on Windows/Amiga builds for now.

02

Point your AI host at the bridge script

{
  "mcpServers": {
    "fred80": {
      "command": "python3",
      "args": ["/path/to/amico-8/tools/mcp_bridge.py"],
      "env": { "FRED80_MCP_TOKEN": "some-long-random-string" }
    }
  }
}

tools/mcp_bridge.py pipes JSON-RPC lines between your host's stdio and FRED-80's TCP socket — all protocol logic and the auth boundary live in the engine, not the bridge. Works with Claude Desktop, Gemini CLI, and any other stdio-based MCP host.

03

Just ask

Open a cart and describe what you want. The assistant reaches for screenshot, sprite_get, or get_error_log the same way it reaches for any other tool — no special prompting required.

What a session actually looks like.

A real tool-call sequence — no hand-waving. The assistant closes the loop itself: draw, run, look, fix.

you: "the player sprite looks wrong and it won't move when I press right"

sprite_get(n=0) → pixels: [...], hitbox: [...] (spots a stray pixel at 3,3)
sprite_set(n=0, x=3, y=3, color=0) → ok
get_error_log() → "main.lua:24: attempt to compare nil with number" (age_ms: 1200)
cart_read_sheet(name="main") → ... finds a typo: btn(RIGHT) instead of btn(right) ...
cart_write_sheet(name="main", content="...") → ok
reload_cart() → ok
inject_input(btn="right", hold_ms=300) → ok
screenshot() → [image] — player sprite fixed, now moving right

40 tools, 8 categories.

Everything the engine already does for a human in the editor, exposed the same way to an assistant.

Diagnostics

5 tools
  • engine_ping
  • get_telemetry
  • get_error_log
  • get_palette
  • docs_search

Sprites

6 tools
  • sprite_get / sprite_set
  • sprite_draw
  • sprite_fill_rect
  • sprite_hitbox_set
  • sprite_export

Tilemaps

9 tools
  • map_load
  • map_get / map_set
  • map_draw
  • map_export
  • iso_set / iso_get
  • iso_fill_height
  • iso_info

Voxels

6 tools
  • voxel_get / voxel_set
  • voxel_fill_plane
  • voxel_fill_row
  • voxel_rebuild_hull
  • voxel_info

Audio

5 tools
  • sfx / music
  • note
  • snd_load / snd_play

3D

1 tool
  • poly_build

Cart & code

6 tools
  • cart_create / cart_open
  • cart_read_sheet / cart_write_sheet
  • cart_list_sheets
  • reload_cart

Play-testing

2 tools
  • inject_input
  • screenshot

Isometric tilemaps, worked.

Diamond-grid tilemaps get their own tool category and their own runtime API — most assistants hit this cold, so here's the whole loop both ways: writing cart code directly, and live-editing through MCP.

Writing cart code directly

local iso = dofile(CART_DIR .. "isometric.lua")
local TW, TH, HU = 32, 16, 8   -- tile width, height, elevation-per-height-unit (px)
local cols, rows = 4, 4
local data, heights, ramps = {}, {}, {}
for i = 1, cols * rows do
    data[i], heights[i], ramps[i] = 1, 0, 0   -- tile 1, ground level, flat
end
local tm = tilemap_new_iso(TW, TH, HU, cols, rows, { ground_sprite }, data, heights, ramps)
local map = iso.new(tm, 320, 240)   -- ox,oy: world (1,1)@height 0 projects here

function _draw()
    cls(0)
    map:draw()                                       -- terrain
    map:draw_actor(player_spr, p.col, p.row, p.h)     -- occlusion-correct actor
end

Full API (ramps, hop-tweens, iso.center_bounds, loading a map the editor painted via iso_mload) is on the API Reference page; a runnable cart is in the examples.

Live-editing through MCP

you: "carve a 3-wide raised platform into the iso map, north side"

iso_info() → {cols:32, rows:32, max_height:8, active_slot:0}
iso_set(col=14, row=2, tile=3, height=2) → ok
iso_set(col=15, row=2, tile=3, height=2) → ok
iso_set(col=16, row=2, tile=3, height=2) → ok
iso_fill_height(col=15, row=3, height=2) → ok (floods the connected plateau behind it to match)
screenshot() → [image] — platform's in, matches the ask

iso_set/iso_get's col/row are 0-based (the editor's raw grid) — a different convention from tilemap_get/tilemap_set's 1-based one at the Lua/runtime layer above. Worth stating up front to an assistant working across both.

What it can't do (on purpose).

Every tool is tagged by how much it can change, so a host can reason about — or ask you to confirm — anything beyond a read.

Diagnostic / Telemetry

Read-only. Screenshots, sprite/palette/error readback, fps and frame timing. Can't change anything.

Mutate

Changes cart state — sprite pixels, tilemap cells, voxels, a played sound, an injected button press. Reversible by re-editing or re-running.

Destructive

Only reload_cart today. Restarts the cart from a fresh Lua state — all in-memory runtime state (score, position) is lost, same as pressing F5.

The server only binds to 127.0.0.1 and requires a bearer token set by whoever launches FRED-80 — it's not reachable over the network, and there's nothing to configure beyond the token. execute_lua_eval (arbitrary Lua from the assistant) is deliberately not shipped yet — it needs real sandboxing first.

Try it yourself

Full tool schemas and parameters

Every tool listed here has a full parameter reference, defaults, and gotchas documented alongside the rest of the Lua API.