Paste is the macOS clipboard app I built for myself. It works well, and I iterate on it quickly, but I soon hit a real efficiency problem: copying and pasting an agent's plan, or a file of some type, is a hassle. So I wondered: can this become automatic? The idea got my heart racing, and I dove into a tug-of-war with the agent.

1. MCP (Model Context Protocol): a cannon for a mosquito

The starting point was concrete. Paste has two kinds of state: the clipboard list, and floating cards pinned to the screen. People create, read, update, and delete them with keyboard and mouse; I wanted the agent to do the same.

I first thought of MCP. Expose the operations as tools, like Craft note which I have been using — Cursor, Claude Desktop, or Codex could connect and call them. I dropped it quickly, because the setup is genuinely painful: every agent environment needs its own config, plus a long-running service. What I wanted was to open a new chat and have the agent already know how to use it, not to wire up a protocol first.

Agents already run commands. If every operation can become:

paste-xxx items search type
paste-xxx cards show <id>

and you add a skill that tells it where the binary is, to read before write, and to parse JSON, you don't need MCP. Install the skill globally, and you never have to worry about the wiring again.

There is a more practical reason. Paste's list is not as simple as opening a database and looking. Memory only keeps the latest thousand items plus all pinned ones; search goes through SQLite FTS and pinyin. Floating cards are real NSPanels, with their own session files, still there after you quit and reopen the app. If a standalone CLI read and wrote clipboard.sqlite3 directly, the list window and the cards on screen would immediately go out of sync. The agent would think something was deleted when it was not.

So the CLI has to talk to the running Paste, not reinvent a data layer.

2. Xcode, the best IDE in the universe: one Run, then Stop

构建快如风,
操作就断月明中,
报错细无声。
 
---最玄学,Grok 4.6

This was the half hour that broke me. The app launched from Xcode Run; the moment the agent touched the CLI, the debug session stopped. Every touch, it stopped. I even suspected I had to install to /Applications to test. That is not it. After installing there is no debugger — the signals and crashes are still there, you just cannot see them.

The first time, the CLI reported Paste is not running, but the socket file was clearly there. The agent runs in a sandbox, so FileManager's Application Support directory points somewhere else, and the CLI looked for controller.sock on the wrong path. Python using $HOME/Library/Application Support/com.eli.Paste/controller.sock could connect. After connecting, the app still died.

Crash logs beat guessing. Twice it was SIGPIPE: when the other end of a Unix socket closes, a default write kills the process; with a debugger attached, that signal stops Run outright. After fixing that, there was another one, and the log was crystal clear:

EXC_BREAKPOINT
queue: com.eli.Paste.controller
_dispatch_assert_queue_fail
_swift_task_checkIsolatedSwift
closure #1 in PasteController.start()

Swift 6 treated the DispatchSource callback inside start() as MainActor, while the callback actually runs on the com.eli.Paste.controller background queue. The isolation assert fires as SIGTRAP, and Xcode shows Stop. Once I had the answer, I had the agent fix it immediately. The fix was to lift accept handling into a nonisolated static, mark it @Sendable, and hop back to the main thread before touching ClipboardStore.

In the middle there was also a stupid compile error: stop() was pasted twice, probably my misclick.

3. How does Surge, the old-school Mac debugging tool, do it?

Surge ships a skill:

Install the skill from the /Applications/Surge.app/Contents/Resources/Skills/ directory using a symbolic link to ensure the skill can be updated along with the application bundle.

My first instinct was that Surge must be CLI underneath — how else would you plug it in? Thinking about it, that does not hold: Surge has been in development for over a decade, long before agents driving CLIs became a thing. Then again, given how fast LLMs are moving, maybe the Surge team threw money at fable5 and rewrote the guts. Who knows.

Then the investigation lit up.

Surge puts a control socket on the machine:

~/Library/Application Support/com.nssurge.surge-mac/internal-controller.sock

surge-cli is another executable inside the app bundle, at:

/Applications/Surge.app/Contents/Applications/surge-cli

The CLI does not implement the proxy. It sends commands to the running Surge and gets JSON back. --raw is for machines; the default is for humans. The remote path is the same protocol over TCP. The protocol has a version number; a mismatch errors out. Tight.

And I found that Surge's Skill does not implement any features at all. It only teaches the agent three things: in what order to find the binary, prefer JSON, read state before changing it, then read again to verify. For example, environment / dump policy first, then the smallest set, then check again.

These three layers are separate:

Surge.app          holds the runtime
Unix socket        the door commands go through
surge-cli          send commands, print results
skill              tell the agent how to issue those commands

A GUI app does not have to become a CLI app to be agent-operable. As long as the runtime will listen, the CLI can stay thin.