AppleBridge lets an AI agent — Claude Code — build, compile, link, and run authentic 68k Macintosh software on a real System 7.6.1, running inside the Basilisk II emulator.

Want to test the Result 1st hand? - Here is one sample, my 1st success with a Text Adventure in MacBinary format: MacVenture.bin

Initially, I planned taking the genius Fort Apocalypse from Steve Hayls as sample, but I am lacking his green lit.

frt_apo2_MACOs7_klein.jpg

That’s sad, as I did hope to do some Assembler finger training porting it over from the Atari 400/800 world onto our beloved Mac68k world. But anyway, let’s proceed with the boring text advanture as a sample.

You write C or 68k assembly on a modern Mac, say what you want in plain language, and the bridge carries the commands across thirty years of computing history to MPW and ToolServer on the classic side — then hands the real output back. The goal is simple to state and hard to earn: talking to a 1990s Macintosh should feel as dependable as a local shell.

Here a quick sample. - I am asking to list the files in the current directory:

C4_directory_list.jpg

A Sidenote: 1991 Macintosh is an exageration, as I am heavily using TCPIP on the COM stack. That wasn’t in place 1991 and 1st test with AppleTalk revealed that this route would become a bit bumpy.

This is an overview of how AppleBridge pulls that off, and how its MCP tools are best put to work — including a few captures taken live from the running bridge while this article was written.

The problem: a brilliant machine with 1991 manners

System 7.6.1 on a 68k Mac is a wonderful, fragile thing. It has a few megabytes of memory, a CPU measured in tens of megahertz, cooperative multitasking that a single misbehaving app can freeze, and a networking stack from the dawn of consumer TCP/IP. None of that was designed to be driven by an autonomous agent issuing build commands over a socket.

Three obstacles stand out, and AppleBridge has a dedicated answer to each:

  1. Networking — the emulator lives behind NAT, so the host can’t dial in.
  2. File sharing — getting source onto the Mac without corrupting it.
  3. Seeing the screen — System 7.6.1 has no screenshot tool.

Architecture: the Mac dials out

flowchart LR
    A["Claude Code
(AI agent)"] --> B["MCP server
(stdio)"] B -->|"control :9001"| C["host_server.py
(:9000 + :9001)"] C -->|"command over TCP :9000"| D["AppleBridge daemon
(System 7.6.1, 68k)"] D -->|"Apple Events"| E["ToolServer / MPW Shell"] E -->|"SC / Asm / Link / Run"| F["68k Macintosh app"] D -.->|"daemon dials OUT first"| C

Because the emulated Mac sits behind NAT, the connection is reversed: the Mac daemon connects out to the host, not the other way around. host_server.py listens on two ports — :9000 for the daemon and :9001 for control and MCP traffic — and simply relays. The wire protocol is deliberately blunt: a request is COMMAND:<len> followed by the payload; a response is a length-framed STATUS / STDOUT / STDERR block that the host reads by declared byte count, not by terminator. That one decision is what lets large compiler listings and multi-kilobyte file dumps survive the trip intact — responses larger than 64 KB stream through a dynamic buffer rather than overflowing a fixed one.

On the Mac side, commands arrive as Apple Events and are dispatched to ToolServer. This matters: the plain MPW Shell will execute a command but returns an empty reply, so the agent is working blind. ToolServer returns real stdout, which is what makes closed-loop debugging — compile, read the error, fix, recompile — possible at all.

Pillar 1 — File sharing without corruption

The host exposes a shared folder that appears on the Mac as the Unix: volume. It’s read-only from the Mac side, so the workflow is: drop converted files in the share, then Duplicate them onto local Mac storage before building.

The subtle part is encoding. The host speaks UTF-8 with \n line endings; the Mac speaks MacRoman with \r. Ship a file across untranslated and the compiler chokes — or worse, an MPW special character like (line continuation), ƒ (folder), or (wildcard) silently turns into garbage. AppleBridge’s encoding_convert.py handles the round trip in both directions, mapping characters and line endings so a file written on the host compiles cleanly on the Mac.

Pillar 2 — Seeing the emulated screen

System 7.6.1 can’t take its own screenshot, and grabbing the host desktop captures the wrong thing entirely. So the daemon captures the emulated screen itself: it reads the main GDevice’s PixMap — pixels, colour depth, and colour table — and streams that raw pixmap over the bridge. The host then decodes it to PNG with a pure-stdlib script, no image libraries required. The result is a pixel-accurate view of exactly what the classic Mac is showing, at full 1024×768.

The seven MCP tools, and how to use them well

Everything above is exposed to any Claude session as seven MCP tools, so there’s no glue code to write:

ToolWhat it does
mpw_executeRun any MPW/ToolServer command, get stdout back
mac_read_fileRead a file off the Mac
mac_write_fileWrite a file onto the Mac
mac_list_filesDirectory listing with type, creator, size, dates
mac_compileCompile a source file
mac_screenshotCapture the emulated screen as PNG
launch_appLaunch a built application

A few hard-won rules make these tools reliable rather than merely available:

  • Link, never ILink. The modern Link -model far is the verified default; ILink also works with the correct library paths — the old “ILink crashes Basilisk II” belief traced to an empty {LIBS}, not the linker.
  • Verify by artifact, not by status. A long Link may report an Apple Event timeout yet still have produced a perfectly good executable — so check that the file exists and has a non-empty resource fork (the CODE resources) — a 0-byte data fork is normal for 68K, don’t trust the return code.
  • Capture stderr with , never 2>&1. The Unix-style redirect crashes the MPW Shell outright; MPW’s own ≥ file.err operator does the job safely.
  • Build off the running daemon. A heavy link inside the same ToolServer that’s serving the bridge can take the bridge down with it — so link to a .new file and swap.

The throughline: prefer the smallest tool that answers the question, and always confirm the result rather than assuming the command’s exit signal told the truth. On a thirty-year-old emulator, the artifact is ground truth.

Live from the bridge

The captures below were taken live while writing this article, through the same MCP tools described above:

  • mpw_execute("Echo HELLO from AppleBridge") → returned HELLO from AppleBridge with STATUS:0 — proof that ToolServer is answering with real output, not an empty reply.
  • mac_list_files("MeinMac:MPW:") → a full directory listing including ToolServer (APPL/MPSX), the MPW Shell, and the AppleBridge build folder.
  • mac_screenshot() → the 1024×768, 8-bit image below, captured from the emulated framebuffer and decoded to PNG on the host.

AppleBridge daemon v0.5.1 running live on System 7.6.1, with the command console showing a request being served and an MPW window open on the build folder

In the daemon window you can see the green Active LED, the command console mid-request, a Screenshot captured! line, and an uptime counter — the small pieces of visible health that turn a silent socket into something you can actually trust.

Why it matters

AppleBridge isn’t really about nostalgia. It’s a small proof that an AI agent can drive genuinely alien hardware — different byte order, different character set, different event model, different failure modes — if the bridge between them is honest about what’s happening and resilient to the rough edges. A 68k Mac with a few megabytes of RAM is about as far from a modern dev container as you can get, and yet, command by command, it answers. That’s the interesting part.