Every scriptable macOS application ships with a hidden API surface, locked inside an .sdef (scripting definition) – which is Apple’s way of saying “a cryptic XML file you’ll definitely hate reading.” Want to automate Notes, Finder, Mail, or Safari? You start by parsing nested <class>, <property>, and <command> tags that are genuinely hostile to humans. I got tired of that particular tax, so I built sdef2md to fix it.
sdef2md is a single-file Go CLI with zero external dependencies that converts .sdef XML into clean, structured Markdown: tables of classes, properties, commands, enumerations, and value types, plus ready-to-use JXA, AppleScript, Go, and Swift invocation patterns.
sdef /System/Applications/Notes.app > notes.sdef
go run sdef2cmd.go < notes.sdef > notes-api.md
The output is readable by humans, but that’s not really the point. It’s designed for LLM agent workflows where an agent needs a structured, traversable API reference to act on rather than raw XML it has to interpret.
The new skills/sdef-to-mcp/ directory takes that one step further: it’s an agent skill that turns any sdef2md-generated Markdown into a complete, compilable Go MCP server. Generate the Markdown reference for any app, feed it to the skill, and it auto-detects the app name, generates tool registrations and handler stubs for every command and class, emits a ready-to-compile Go project with JXA script templates, and wires in error handling for TCC permissions, timeouts, and missing objects. The resulting server speaks stdio JSON-RPC, plugs into Claude Desktop or any other MCP client, and bridges to the target app via osascript — no direct Apple Events API access required.
Before this, building an MCP server for a macOS app meant manually reverse-engineering Apple Events, writing brittle AppleScript from memory, and guessing at object containment hierarchies until something stopped crashing. Now it’s two commands and a scaffold you adapt rather than a blank page you fill.
Every generated project includes main.go (MCP server setup via mark3labs/mcp-go), jxa/runner.go (osascript exec wrapper with timeout and error parsing), tools/*.go (handlers for each detected command and class), and a README.md with build instructions and an adaptation checklist. The JXA scripts are starters, not production code – every one is tagged // REVIEW: adapt this for <AppName> — so you’ll still need to verify property names, containment hierarchies, and command parameters for your specific target. But you’re starting from something that compiles and has the right shape, which is most of the work.
git clone https://git.sr.ht/~hrbrmstr/sdef2md
cd sdef2md
go run sdef2cmd.go < any-app.sdef > any-app-api.md
If you’ve been wanting to script a macOS app from an LLM agent workflow, this removes most of the boilerplate. What’s left is just adapting the generated JXA to the quirks of your specific target – and at least you know exactly what those quirks are, because the Markdown told you.
MIT license. Repo at https://git.sr.ht/~hrbrmstr/sdef2md.