Build an MCP client to your own Foundation Model app

Here's another (longish) update on LocalLM Lab, which I've mentioned here previously.

v0.7 ships the LocalLM Lab SDK: you can now build your own app on Apple's on-device Foundation Models model, using the same MCP client LocalLM Lab uses to talk to real tools and data like Slack, Todoist, GitHub, Notion, Linear and others. And there are connectors to Calendar/Reminders/Contacts/Location access. Most importantly, you can subsequently ship your app including through the Mac App Store.

The toolkit that shipped with the v0.6 update was good at programmatic experimentation. This is specifically because calls to `localai-cli` were relayed through LocalLM Lab's background process over a local socket. This meant the LocalLM Lab app had to be installed and running. The SDK doesn't relay through anything. It's `LocalLMLabSDKCore`, a binary xcframework that links `FoundationModels` and the MCP client directly into your own application binary in a self-contained manner.

Distribution: SPM `binaryTarget`, pulled from a GitHub Release with a checksum, pinned to an explicit version:



.binaryTarget(

    name: "LocalLMLabSDKCore",

    url: "https://github.com/ancientcomputing/locallm/releases/download/vX.Y.Z/LocalMLabSDKCore-X.Y.Z.xcframework.zip",

    checksum: "..."

)

The SDK requires Swift 6, macOS 26+ on Apple Silicon. Licensed Apache 2.0, specifically for the patent grant.

Core API surface: MCPServerManager exposes addServer(url:displayName:authType:patToken:manualClientID:) async -> Result<MCPServerState, MCPServerError>, callTool(server:tool:arguments:) async -> Result<String, MCPServerError>, toolsForSession() -> [MCPToolDescriptor], setToolEnabled(server:tool:enabled:), plus an AsyncStream of server-state changes for observing connections without polling. MCPAuthType is .none | .pat | .oauthManual; MCPServerError has explicit cases including .oauthRegistrationNotSupported (retry with .oauthManual when a server doesn't support dynamic client registration. This is same DCR gap Slack had in the v0.4 writeup). Resources and prompts get their own methods (resourcesForSession/ readResource, promptsForSession/ getPrompt), not folded into the tool-calling path.

Connectors are a unified facade: Connectors.isAuthorized(_:), .requestAccess(_:) async, .openSystemSettings(for:) across a Connector enum (calendar, reminders, contacts, location); with per-connector types for the actual data shapes (CalendarAccess.upcomingEvents(days:)/ .addEvent(...), RemindersAccess.upcomingReminders(days:) async/ .addReminder(...), ContactsAccess.search(query:limit:), LocationAccess.shared.currentLocation() async). Weather and Clock are zero-permission built-in tools, not connectors with no auth flow at all. And Calendars/Reminders/Contacts are still read/add-only with no edit/write capability. Should that be the next step?

OAuth is mandatory-configured, not automatic: MCPOAuthFlow.redirectURI plus a registered URL scheme in Info.plist, and redirects have to be handled in application(_:open:) which is the app delegate callback and not SwiftUI's .onOpenURL. Tokens land in Keychain automatically, scoped to bundle.main.bundleIdentifier via native Keychain Services (not a custom store).

Sandbox story: we built the example Plate Today app into a sandboxed test app and verified working, with a signed path to a Mac App Store `.pkg` (Apple Distribution signing + provisioning profile pipeline). One entitlement is worth flagging loudly: `com.apple.security.network.client` is easy to miss and fails silently, not with a thrown error. Without it, MCP connections and Weather calls will just hang.

There's also an optional LocalLMLabSDKComponents package: drop-in SwiftUI (MCPServerPickerView, MCPOAuthWaitingView, MCPResourcesView, MCPPromptsView, an ObservableObject wrapper). This is a useful reference if you don't want to build your own MCP server-management UI from scratch.

Two reference apps are included in the repo. Both requiring an explicit version pin to build (`LOCALLM_SDK_VERSION=0.7.1 swift build`): `plate-today` and `components-demo`.

A few known gaps: (1) No filesystem connector in Core. Bring your own picker. (2) No public API stability guarantee yet. (3) Contacts is untested under sandbox. (4) Location's reverse-geocoding has some pre-existing bugs. (5) And the read-only/add-only capability mentioned earlier here.

SDK guide: https://github.com/ancientcomputing/locallm/blob/main/docs/sdk-guide.md

Feature page: thisbrain.ai/locallm/sdk.html

Hopefully, this will encourage folks to do more with Apple's Foundation Models. Worth noting: Microsoft's Phi Silica is already being replaced by "Aion Instruct," with removal set for November 2026. Apple's on-device story isn't mid-transition like that and it's only going to get better with macOS 27.

Quick 0.8 update on this. The SDK now ships ready-made Tool wrappers for every connector (Calendar, Reminders, Contacts, Location etc) and new this release, Filesystem via WorkspaceTools. Previously, developers had to write their own adapters (which remains fully supported alongside the new tools).

We ended up going the Tool wrapper route because Apple's local AI (at the macOS 26 level) wasn't reliable at mapping (to give an example) a human-readable Calendar event (Lunch on August 25) to the event ID that the connector used to require to perform any action on that event.

Question: isn't that Apple's Tool protocol reimplemented? Well, it's Apple's Tool protocol used the way it's meant to be, with the annoying parts done for you. That means handling connector-level footguns like the identifier problem above so you don't have to rediscover them yourselves, plus one piece Apple doesn't provide at all: MCPTool, which builds a Tool at runtime straight from an MCP server's JSON Schema. FoundationModels has zero MCP support of its own. Short version: we're not replicating Apple's Tools, we're using them, and taking care of the parts that were genuinely annoying to get right.

Worth noting: filesystem access itself (the picker, security-scoped bookmarks) is still deliberately host-app territory, not part of the SDK. Everything downstream of that (once your app has a resolved folder URL) is where WorkspaceAccess/ WorkspaceTools come in. This includes an async-aware access pattern (withFolderAccessAsync) that is needed since a model session can invoke several file tools across one respond(to:)call. The new example (workspace-buddy) shows it in context.

Repo: GitHub - ancientcomputing/locallm: Public LocalLM Lab repository. Code samples can be found here. · GitHub