Standalone Swift package for hosting the local SpeakSwiftly runtime behind an app-friendly HTTP API, an optional MCP surface, and a small embedded Apple-platform API.
- Overview
- Quick Start
- Usage
- Development
- Repo Structure
- Release Notes
- License
- Embedding
- Configuration
- Codex Plugin
This project is actively available and stable enough to try.
SpeakSwiftlyServer is the standalone Swift Package Manager home for the local SpeakSwiftly server layer. It ships one reusable library target for embedding and one executable target, SpeakSwiftlyServerTool, for running the shared localhost service, LaunchAgent maintenance commands, and health checks.
The package exposes three user-facing surfaces:
- a localhost HTTP API for app and operator control
- an optional MCP surface for tool, resource, and prompt access
- a small embedded Apple-platform API centered on the public
EmbeddedServerobservable model
The goal is to give macOS and near-future Apple-platform apps one small, typed local speech-service layer without adding a second runtime stack or forcing every consumer to rebuild the same transport and lifecycle glue around SpeakSwiftly.
Build the package with Xcode's selected Swift toolchain:
xcrun swift buildRun the shared server executable locally:
xcrun swift run SpeakSwiftlyServerToolCheck the current operator surface:
xcrun swift run SpeakSwiftlyServerTool help
xcrun swift run SpeakSwiftlyServerTool healthcheck --base-url http://127.0.0.1:7338For contributor setup, validation, release workflow, and live end-to-end coverage, use CONTRIBUTING.md.
Run the server directly in the foreground:
xcrun swift run SpeakSwiftlyServerTool serveInstall or refresh the per-user LaunchAgent with a config file:
When the default staged tool path is used, this command first builds and stages the current checkout at .release-artifacts/current/SpeakSwiftlyServerTool, refreshes its bundled Metal resource, refreshes the staged ad-hoc signature, and then writes and bootstraps the LaunchAgent. Pass --tool-executable-path /path/to/SpeakSwiftlyServerTool only when you intentionally want to install a specific prebuilt executable instead.
xcrun swift run SpeakSwiftlyServerTool launch-agent install \
--config-file ./server.yamlUse the explicit promotion command when you want the lower-level "build, stage, then reinstall" spelling. This is mostly useful for release or operator scripts that want to name the promotion step directly; ordinary default-path refreshes can use install.
xcrun swift run SpeakSwiftlyServerTool launch-agent promote-live \
--config-file ./server.yamlInspect or remove the installed LaunchAgent:
xcrun swift run SpeakSwiftlyServerTool launch-agent status
xcrun swift run SpeakSwiftlyServerTool launch-agent uninstallThe package uses distinct default localhost ports by entrypoint:
- direct executable startup defaults to
127.0.0.1:7338 - LaunchAgent installs default to
127.0.0.1:7337 - embedded app-owned sessions default to
127.0.0.1:7339
The full transport contract lives in API.md.
The contributor and maintainer workflow lives in CONTRIBUTING.md.
Use that guide for:
- local setup and runtime expectations
- validation commands
- live end-to-end coverage
- pull request and release workflow
- monorepo and submodule handoff rules
The short version is:
- use
xcrun swift testfor the normal package-development loop - use
sh scripts/repo-maintenance/validate-all.shfor the full maintainer and CI gate - use
scripts/repo-maintenance/release.sh --mode standard --version vX.Y.Z --skip-version-bumpfor the aligned release flow - use
scripts/repo-maintenance/config/profile.envto confirm the activeswift-packagemaintainer profile
Resolve package dependencies with the Xcode-selected Swift toolchain:
xcrun swift package resolveInstall the local tools used by the full maintainer gate when you are running it outside CI:
brew install swiftformat swiftlintUse a feature branch for normal repo work. Keep Swift package changes grounded in Package.swift, keep source and docs updates together when public behavior changes, and use CONTRIBUTING.md for pull request, live-service, and monorepo handoff rules.
Run the full local maintainer gate before handing off a complete change:
sh scripts/repo-maintenance/validate-all.shFor a narrower package-development loop, run:
xcrun swift build
xcrun swift test.
├── Sources/
│ ├── SpeakSwiftlyServer/
│ └── SpeakSwiftlyServerTool/
├── Tests/
├── docs/
├── API.md
├── CONTRIBUTING.md
├── Package.swift
└── README.md
Sources/SpeakSwiftlyServer/contains the reusable library target.Sources/SpeakSwiftlyServerTool/contains the unified executable wrapper.Tests/contains unit, integration, and a small opt-in live E2E smoke suite.docs/contains maintainer-facing supporting documentation.
Tagged release notes live in GitHub Releases and the repo keeps matching historical release notes and release checklists under docs/releases. Investigations and incident writeups live under docs/investigations.
See LICENSE.
The supported public embedding surface is EmbeddedServer, defined in Sources/SpeakSwiftlyServer/Host/ServerState.swift. App code owns that one observable object directly, calls liftoff(), binds UI to its observable properties, and uses the same object for runtime controls, playback controls, voice-profile actions, and direct live speech submission through queueLiveSpeech(...), including the shared SpeakSwiftly.RequestContext metadata model when one request needs caller-origin details.
import SpeakSwiftlyServer
import SwiftUI
@main
struct ExampleApp: App {
@State private var server = EmbeddedServer(
options: .init(
port: 7811,
runtimeProfileRootURL: FileManager.default
.urls(for: .applicationSupportDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("ExampleApp/SpeakSwiftlyRuntime", isDirectory: true)
)
)
var body: some Scene {
WindowGroup {
ContentView(server: server)
.task {
try? await server.liftoff()
}
}
}
}If you do not pass EmbeddedServer.Options(port:), the embedded host defaults to 127.0.0.1:7339. If you pass EmbeddedServer.Options(runtimeProfileRootURL:), the server treats that as its profile-store root and bridges it at startup into the broader persistence root expected by the current pinned SpeakSwiftly runtime, while keeping the server's own runtime-configuration snapshot aligned with the same on-disk state.
The shared server supports these environment variables:
APP_CONFIG_FILEAPP_NAMEAPP_ENVIRONMENTAPP_DEFAULT_VOICE_PROFILE_NAMEAPP_HOSTAPP_PORTAPP_SSE_HEARTBEAT_SECONDSAPP_COMPLETED_JOB_TTL_SECONDSAPP_COMPLETED_JOB_MAX_COUNTAPP_JOB_PRUNE_INTERVAL_SECONDSAPP_HTTP_ENABLEDAPP_HTTP_HOSTAPP_HTTP_PORTAPP_HTTP_SSE_HEARTBEAT_SECONDSAPP_MCP_ENABLEDAPP_MCP_PATHAPP_MCP_SERVER_NAMEAPP_MCP_TITLESPEAKSWIFTLY_PROFILE_ROOT
If APP_CONFIG_FILE points at a YAML file, the server loads it through the package's Foundation URL-backed YAML provider and swift-configuration, with environment variables taking precedence over YAML and YAML taking precedence over built-in defaults. Missing config files fail startup loudly. LaunchAgent install and refresh paths seed the default ~/Library/Application Support/SpeakSwiftlyServer/server.yaml from the bundled template when that canonical file is missing.
app:
name: speak-swiftly-server
environment: development
host: 127.0.0.1
port: 7338
sseHeartbeatSeconds: 10
completedJobTTLSeconds: 900
completedJobMaxCount: 200
jobPruneIntervalSeconds: 60
http:
enabled: true
host: 127.0.0.1
port: 7338
sseHeartbeatSeconds: 10
mcp:
enabled: false
path: /mcp
serverName: speak-swiftly-mcp
title: Speak SwiftlyThe app-managed install layout is centered on one per-user location under ~/Library/Application Support/SpeakSwiftlyServer, with logs in ~/Library/Logs/SpeakSwiftlyServer. The package exposes that layout directly through AppManagedInstallLayout.swift.
This repository is also packaged as a repo-local Codex plugin through .codex-plugin/plugin.json. The plugin points at the checked-in .mcp.json connection for the local speak_swiftly MCP server and the tracked skills bundle that teaches Codex how to use the surface intentionally.
The first plugin pass ships focused skills for:
- broad MCP orientation
- LaunchAgent setup and maintenance
- runtime, playback, and queue control
- voice workflows
- text-profile workflows