A Swift implementation of the Agent Client Protocol
(ACP) in a single module — import SwiftACP — covering all three roles:
- the client — the protocol types + a JSON-RPC client (
ACPAgent/ACPAgentConnection) for driving an ACP agent (the editor/host side; spawning is desktop-only). - the server — the agent/server harness for exposing an app or CLI as an ACP
agent (
ACPAgentHandler,ACPServerSession,ACPAgentServer). - the daemon client — the generated
ACPXDaemon.Clientfor driving a remoteacpxdsession daemon over MCP, on every platform including iOS and Android.
The library builds on
JSONFoundation (zero-dependency:
JSON value/schema types and the JSON-RPC runtime) and
SwiftMCP's swift-nio-free MCP client, so
it embeds anywhere — a Mac app, an iOS app, an agent CLI, a test. The same package
also ships the acpx CLI and acpxd daemon (macOS-only —
see below), a headless toolkit for driving
ACP agents modelled after the original openclaw/acpx.
.package(url: "https://github.com/Cocoanetics/SwiftACP.git", from: "0.1.0"),with "SwiftACP" in your target's dependencies. The default-on Server package
trait pulls SwiftMCP's swift-nio server transports (what acpxd serves over). A
client-only consumer — an iOS or Android app driving a remote acpxd — should
disable it for a swift-nio-free graph:
.package(url: "https://github.com/Cocoanetics/SwiftACP.git", from: "0.1.0", traits: []),import SwiftACP
struct MyHandler: ACPAgentHandler {
func initialize(_ request: InitializeRequest) async -> InitializeResponse {
InitializeResponse(agentInfo: Implementation(name: "my-agent", version: "1.0"))
}
func newSession(_ request: NewSessionRequest) async throws -> NewSessionResponse {
NewSessionResponse(sessionId: UUID().uuidString)
}
func prompt(_ request: PromptRequest, session: ACPServerSession) async throws -> PromptResponse {
await session.sendText("Hello!")
return PromptResponse(stopReason: .endTurn,
usage: PromptUsage(inputTokens: 10, outputTokens: 2, totalTokens: 12))
}
}
@main enum Main {
static func main() async throws {
try await ACPAgentServer.serveStdio(handler: MyHandler()) // speaks ACP over stdin/stdout
}
}Only initialize, newSession, and prompt are required; authenticate,
loadSession, cancel, setMode, setConfigOption, setModel, and
availableCommands have defaults. ACPServerSession streams session/updates
(text, reasoning, tool calls, plans), calls back to the client — permission prompts
(requestPermission) and file I/O (readTextFile / writeTextFile) — and exposes
cooperative cancellation. LoopbackTransport.pair() (JSONFoundation's in-memory
transport, re-exported by SwiftACP) runs a client and server in the same process
for embedding an agent inside an app or for hermetic tests.
import SwiftACP
let agent = try await ACPAgent.launch(agent: "claude", cwd: repoPath, permission: .approveReads)
let session = try await agent.newSession()
let outcome = try await session.run("Explain this project") { update in render(update) }
print(outcome.text, outcome.stopReason)
await agent.close()The same package ships a headless CLI and a session daemon built on the library — a
byte-faithful Swift clone of openclaw/acpx 0.11.0.
They're macOS-only (Bonjour service advertisement, POSIX signals) and are gated
behind #if os(macOS) in Package.swift, so the SwiftACP library itself stays
nio-free and keeps building on Linux and Windows.
swift run acpx claude "explain what this project does"
swift run acpx codex --approve-reads "find and fix the flaky test"
git diff | swift run acpx claude -q "review this diff"
swift run acpx chat codex # interactive multi-turn session
swift run acpx agents # list known agents + resolved launch commandsacpxd is the session daemon: an MCP server (Bonjour + local TCP) holding live ACP
sessions, with an optional outward HTTP+SSE transport.
swift run acpxd # Bonjour + local TCP (how the acpx CLI discovers it)
swift run acpxd --http-port 9090 -v # also expose MCP over HTTP+SSE (unauthenticated — keep on loopback)The CLI and daemon are built on the library plus SwiftMCP's server side: acpxd is
an @MCPServer whose tools the CLI calls over MCP — the same generated
ACPXDaemon.Client an iOS app uses to drive a remote daemon. The extra
executable-only dependencies (service-lifecycle, argument-parser) never reach
consumers of the SwiftACP library product.
A byte-faithful Swift clone of
openclaw/acpx, validated by loopback + a mock
agent driven by the real ACP client (Tests/ACPTests/Fixtures/mock-agent.py).
SwiftAgents' Coder example exposes itself over ACP via the server half.
BSD 2-Clause — see LICENSE.