Everything that happened on github.com/Cocoanetics since the SwiftText post — and it’s more than I expected to fit into three weeks: a foundation package that didn’t exist on June 23 and now carries three protocols, SwiftMCP turned inside out, two brand-new projects, releases across the whole mail stack — and a very welcome comeback doing the final polish.
The third time you vendor a JSON type, it wants to be a foundation
Last month I wrote that the third time you copy something, it wants to be a package. It turns out the rule applies one level further down. SwiftMCP carried its own JSON value type, its own JSON Schema model for describing tools to LLMs, and its own JSON-RPC envelope. SwiftAgents had grown a JSONValue of its own, complete with subscripts and literal builders. And the moment I started eyeing other JSON-RPC protocols, I could see myself copying all of it a third time.
So on June 24 I extracted JSONValue from SwiftMCP as a dependency-free package. By the end of that same day it had absorbed the JSON Schema model and the JSON-RPC 2.0 types — and a better name: JSONFoundation.
What followed was ten days of the most satisfying kind of work: teaching one package to do properly what three packages had each done approximately. A spec-complete JSON-RPC envelope — params and results can be any JSON value now, not just objects, because ACP peers really do return bare null. A transport-agnostic peer that owns correlation and dispatch. And a whole family of transports: stdio, a zero-dependency POSIX TCP client, HTTP+SSE in both directions, and an in-memory loopback pair for tests that never touch a pipe. Nine opt-in products, zero third-party dependencies in the core, building on macOS, iOS, Linux, Windows, and Android. Fourteen releases, 1.0.0 through 2.5.0.
The insight that makes it all click: MCP, ACP, and LSP are the same protocol wearing different clothes. Same envelope, same request/response correlation, same notification semantics. They differ on exactly one axis — how messages are framed on the wire. MCP over stdio wants newline-delimited JSON. LSP wants HTTP-style Content-Length headers. Streamable HTTP wants SSE events. So the framing became a pluggable codec, and everything above it is shared.
The hard part was HTTP+SSE
SwiftMCP is where all of this came from, and SwiftMCP is where it had to prove itself. Four releases went out on July 1 — 1.7.0 through 1.9.0 — and together they turn the framework inside out.
The stdio and TCP transports moved onto JSONFoundation’s shared runtime without much drama; no more hand-rolled correlation or framing, and the in-process transport doesn’t even open OS pipes anymore. The HTTP+SSE server was the hard part. It always is. Sessions, replay buffers, resume-after-disconnect, and Swift NIO’s tendrils reaching into everything. The 1.9.0 answer: a new MCPHTTPEngine seam that owns routing, sessions, and SSE — with NIO demoted to just one adapter behind it. There’s an in-memory adapter now that drives the whole HTTP stack in tests without opening a socket, and the SSE replay/resume machinery lives in JSONFoundation where ACP and LSP can use it too. The point of the exercise: NIO is now swappable. Network.framework, or another Swift HTTP server, can slot in behind the same engine — that’s the door this refactor opens.
With the architecture settled, SwiftMCP promptly got ahead of its own protocol: the phases of the upcoming MCP 2026-07-28 spec revision — server/discover, sessionless Streamable HTTP, Origin allowlisting against DNS rebinding — are already implemented, unadvertised until the final flip.
Because the foundation held, I got greedy: ACP
With the JSON-RPC runtime feeling this solid, I went looking for other protocols to point it at. The obvious first target: the Agent Client Protocol — the protocol editors like Zed use to talk to coding agents. SwiftACP covers all three roles in one import: drive an ACP agent from Swift, expose your own app or CLI as an agent, or talk to the bundled acpxd daemon remotely. That last one still makes me grin: an iPhone holding a live coding-agent session that’s actually running on the Mac.
The proof-of-concept came full circle in SwiftAgents: the bundled Coder example now serves ACP with a single subcommand — coder acp — complete with slash commands and a native model menu, so Zed shows a dropdown that hot-switches between OpenAI, Anthropic, and Google mid-session.
And then the disappointment. Neither Codex nor Claude actually speak ACP natively — unlike so many other coding agents. What acpx claude really launches is a wrapper, and in Claude’s case that wrapper sits on the Claude Agent SDK (formerly the Claude Code SDK), which trails the CLI: no memory, and no ultracode — the new mode where Claude authors a massive workflow with resumable parts and supervises it. So I keep pondering building my own harness directly around the claude CLI to get at those features. The building blocks are all sitting right there.
LSP, because agents deserve a compiler’s eyes
The second new protocol target started as a hundred lines of Python — a throwaway probe to crack how sourcekit-lsp frames its messages (Content-Length headers, as it turns out, not the newline-delimited JSON of MCP and ACP). Once the framing was understood, the Swift version practically wrote itself, because LSP contains no transport code at all: the peer, the framing codecs, the process transport — all JSONFoundation. It’s the proof that the unified runtime generalizes.
What you get is compiler-grade Swift code intelligence, packaged two ways: a CLI with nine subcommands (where finds a symbol project-wide off the index, check reports syntax and semantic errors without a build, plus declarations, references, hover) and lsp mcp, an MCP server exposing seven tools an agent can call — backed by one long-lived sourcekit-lsp whose index stays warm across calls. It’s built for agents in the small ways that matter: it trims the trailing newlines agents love to send, streams indexing progress so a cold call doesn’t block silently, and respawns sourcekit-lsp if it dies.
And then we got Fable back
I pointed it — with ultracode — at JSONFoundation for a polish pass: ten review passes over the whole project, every finding adversarially verified before being accepted. 82 findings survived. Eight were genuine bugs: TCP writes that could SIGPIPE-kill the host process, JSON-RPC batches silently truncated to their first message, the @Schema macro stripping umlauts and emoji out of descriptions. The test suite grew from 60 to 137 tests, and 2.5.0 went out today as the polish release. LSP got the same treatment — two multi-agent review rounds, 56 verified findings, from leaked processes to a 30-second stall that became a bounded 3-second wait.
A task like this runs for hours and consumes millions of tokens. But this model, coupled with ultracode, considers scenarios other agents simply miss — the evidence is all over the recent commit history. My job in it was reading verdicts and saying yes.

Meanwhile, elsewhere in the org
- SwiftText 2.0.0 (July 1) — the follow-up to “learning to write” is escaping the Mac: the
swifttextCLI now builds and runs on macOS, Linux, and Windows, defaulting to the pure-Swift render engine off macOS. Numbers and Keynote readers complete the iWork family.brew install cocoanetics/tap/swifttext. - SwiftMail 1.8.0 (July 2) — calendar invites now go out inline per RFC 6047, so Apple Mail, Outlook, and Gmail show actual Accept/Decline buttons instead of a bare .ics attachment. Non-ASCII subjects and names finally get proper RFC 2047 encoding. IMAP got tougher: verified IDLE teardown, coalesced authentication, configurable parser buffers.
- Post 1.6.8 (July 2) — the payoff release where the whole stack lands at once: SwiftMail 1.8, SwiftText 2.0, SwiftMCP 1.9. Working calendar invites, correct international headers, and an MCP daemon that survives disconnects. Getting there required pinning dependencies to exact release commits, because SwiftPM refuses to resolve revision-pinned transitive dependencies through a version requirement — a war story of its own.
brew install cocoanetics/tap/post - SwiftPorts — porting
ghto Swift means porting its whole platform surface:gh auth loginnow actually persists tokens on Linux (Secret Service), Windows (Credential Manager), and Android (encrypted file store). - GitKit and QMDKit settled into maintenance: warning-free umbrella headers downstream, and a two-file migration to SwiftAgents’ new Credential abstraction.
The real narrowing
Last month I said my remaining job is noticing that a thing wants to be a kit and saying so. This cycle narrowed it further. The kits now share one wire model, and the pattern for a new protocol has become almost mechanical: pick the framing, write the typed methods, done — LSP went from Python probe to published package in a week that way. The humans set the what; the agents handle the how; and increasingly, the foundation handles the again.
If you’re building a coding agent: please speak ACP natively. I have a package for you now.
Related
Categories: Updates

