Skip to content
kochj23Public

About

Local AI coding assistant for macOS on Apple Silicon: in-process MLX (mlx-swift-lm) with native tool calling and Stop, 14 tools, Xcode extension, widget. AI via NovaAIKit: on/off, Auto failover across MLX, Ollama, TinyLLM, TinyChat, OpenWebUI, Nova Gateway, OpenRouter, OpenAI; Keychain-only keys.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Latest commit

 

History

147 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MLX Code

Build Platform License Swift Apple Silicon Tests Version

A local AI coding assistant for macOS, powered by Apple MLX. By default nothing leaves your Mac: the model runs in-process on your GPU. When you want more, the same chat can use Ollama, TinyLLM, TinyChat, OpenWebUI, the Nova Gateway, OpenRouter or OpenAI through NovaAIKit, with Auto failover.

MLX Code runs language models directly on Apple Silicon using mlx-swift and mlx-swift-lm 3.x. It provides a chat interface with 14 built-in tools that can read files, search code, run shell commands, build Xcode projects, manage Git repos and work with GitHub.

Written by Jordan Koch.


Contents


Architecture

graph TB
    subgraph Views["SwiftUI Views"]
        ChatView["ChatView<br/>+ NovaAIStatusIndicator"]
        SettingsView["SettingsView<br/>AI tab = NovaAISettingsView"]
        PromptTemplatesView
        GitHubPanelView
        CodeAnalysisPanelView
        OnboardingView
    end

    subgraph ViewModels
        ChatVM["ChatViewModel"]
        ProjectVM["ProjectViewModel"]
        GitHubVM["GitHubViewModel"]
        CodeAnalysisVM["CodeAnalysisViewModel"]
    end

    subgraph Tools["Tool Execution Layer - 14 tools"]
        direction LR
        Core["Core: File Ops, Bash, Grep, Glob, Edit"]
        Dev["Dev: Xcode, Git, GitHub, Code Nav,<br/>Code Analysis, Error Diagnosis,<br/>Test Gen, Diff Preview, Help"]
    end

    subgraph AI["AI layer"]
        AppAI["AppAI.shared<br/>(NovaAI, appID mlxcode)"]
        Kit["NovaAIKit 1.0.0<br/>Auto failover, retries, timeouts,<br/>validation + fallback, Keychain keys"]
        Local["NovaAIClosureModel<br/>(mlx backend = in-process)"]
    end

    subgraph Engine["In-process inference"]
        MLXService["MLXService (actor)"]
        Generator["InferenceGenerator (protocol)"]
        MLXLM["mlx-swift-lm 3.x ModelContainer"]
        ContextManager["ContextManager (actor)"]
        UserMemories["UserMemories (actor)"]
    end

    subgraph Remote["HTTP backends"]
        Ollama
        TinyLLM
        TinyChat
        OpenWebUI
        Nova["Nova Gateway / Fleet"]
        OpenRouter
        OpenAI
    end

    subgraph Security
        CommandValidator
        ModelSecurityValidator["SafeTensors-only Validator"]
        Keychain["macOS Keychain<br/>com.jkoch.mlxcode.*"]
        SecureLogger
        RepDetector["RepetitionDetector"]
    end

    subgraph External["Extensions"]
        XcodeExt["Xcode Extension - 5 commands"]
        Widget["Desktop Widget - 3 sizes"]
        NovaAPI["Nova API - port 37422"]
    end

    Views --> ViewModels
    ViewModels --> Tools
    ChatVM -->|"backend = MLX"| MLXService
    ChatVM -->|"Auto / other backends"| AppAI
    AppAI --> Kit
    Kit --> Local --> MLXService
    Kit --> Remote
    Kit --> Keychain
    MLXService --> Generator --> MLXLM
    ContextManager --> AppAI
    Views -.-> External
    NovaAPI --> ChatVM
Loading

Chat routing

flowchart TD
    Send["User sends a message"] --> Ctx["ContextManager assembles<br/>system prompt + history + project"]
    Ctx --> On{"Use AI on?"}
    On -- no --> Notice["Transcript: 'AI is off' notice<br/>(nothing sent)"]
    On -- yes --> Which{"Backend"}
    Which -- "MLX (default)" --> Loaded{"Model loaded?"}
    Loaded -- no --> Ask["Ask the user to load a model<br/>or pick another backend"]
    Loaded -- yes --> InProc["MLXService.chatCompletion<br/>token streaming, native ToolSpecs, Stop"]
    Which -- "Auto / Ollama / TinyLLM / TinyChat /<br/>OpenWebUI / Nova / OpenRouter / OpenAI" --> Stream["AppAI.shared.stream(request)<br/>failover, retries, timeouts"]
    Stream -- ok --> Reply["Streamed reply"]
    Stream -- "all down" --> Err["Transcript: '⚠️ No reply: reason'<br/>UI stays usable"]
    InProc --> Reply
    Reply --> ToolsQ{"Tool calls?"}
    ToolsQ -- yes --> Run["Approve + run tools,<br/>continue the turn"]
    ToolsQ -- no --> Done["Saved conversation"]
Loading

Inference path (mlx-swift-lm 3.x)

flowchart LR
    subgraph App["MLX Code"]
        VM["ChatViewModel<br/>generationTask"]
        Settings["AppSettings<br/>KV cache: bits / max size / prefill step"]
        Params["ModelParameters<br/>temp, topP, topK, minP,<br/>repetition penalty + context"]
        Svc["MLXService (actor)<br/>busy flag, cancellation,<br/>tool-call mapping"]
        Mapper["GenerationParameterMapper"]
        Specs["ToolSpecBuilder<br/>ToolRegistry → [ToolSpec]"]
        Fmt["NativeToolCallFormatter<br/>toolCall → &lt;tool&gt;{json}&lt;/tool&gt;"]
    end
    subgraph LM["mlx-swift-lm 3.32"]
        Loader["LLMModelFactory.loadContainer(from: URL,<br/>using: #huggingFaceTokenizerLoader())"]
        Container["ModelContainer.prepare / generate"]
        Stream["AsyncStream&lt;Generation&gt;<br/>.chunk / .toolCall / .info"]
    end
    subgraph HF["Hugging Face"]
        Hub["HubClient.downloadSnapshot<br/>*.safetensors *.json *.jinja *.txt *.model"]
        Tok["swift-transformers Tokenizers<br/>(chat template)"]
    end

    Hub --> Loader
    Tok --> Loader
    VM --> Svc
    Specs --> Svc
    Params --> Mapper
    Settings --> Mapper
    Mapper -->|GenerateParameters| Container
    Svc --> Container --> Stream
    Stream -->|chunks| Svc
    Stream -->|tool calls| Fmt --> Svc
    Svc -->|stream to UI| VM
    VM -. "Stop: Task.cancel()" .-> Svc
Loading

Features

Native on-device inference (mlx-swift-lm 3.x)

  • Real Stop: Stop cancels the generation task; token generation ends inside mlx-swift-lm (or the HTTP stream is closed for a remote backend) and the next request starts immediately. The partial answer is kept.
  • Native tool calling: the tool registry is sent to the model's chat template as ToolSpecs, and Generation.toolCall events (Qwen, Llama, Mistral, Gemma, Harmony and more) feed the approval flow, several calls per turn. The <tool>…</tool> text protocol is the fallback, and it's also what remote backends use.
  • KV-cache controls: Settings › Model: KV quantization (none / 8-bit / 4-bit), max KV size (rotating cache), prefill step size.
  • Full sampling controls: temperature, top-p, top-k, min-p, repetition penalty (default 1.1) and repetition context size.
  • Complete downloads: chat templates (*.jinja), BPE merges (*.txt) and SentencePiece models (*.model) come with weights and configs.
  • Context detection: the context window is read from config.json, including nested text_config.

14 built-in tools

Tool Purpose Tier
File Operations Read, write, edit, list, delete files Core
Bash Run shell commands Core
Grep Search file contents with regex Core
Glob Find files by pattern Core
Edit Apply targeted file edits Core
Xcode Build, test, clean, archive, deploy Dev
Git Status, diff, commit, branch, log, push, pull Dev
GitHub Issues, PRs, branches, credential scanning Dev
Code Navigation Jump to definitions, find symbols Dev
Code Analysis Metrics, dependencies, lint Dev
Error Diagnosis Analyze and explain build errors Dev
Test Generation Create unit tests from source files Dev
Diff Preview Show before/after file changes Dev
Help List available commands and usage Dev

Read-only tools auto-approve. Write and execute tools need confirmation.

Slash commands

/commit    /review    /test      /docs
/refactor  /explain   /optimize  /fix
/search    /plan      /help      /clear

/review, /test, /docs, /refactor, /explain, /optimize and /plan ask the selected AI backend (through NovaAIKit). /search, /index, /help and /clear don't need a model.

Prompt engineering toolkit

15 curated prompt templates across 9 categories (Review, Debug, Generate, Refactor, Test, Document, Security, Performance, Deploy). Browse, fill variables, preview and send, all in-app.

Xcode Source Editor Extension

Select code in Xcode and use Editor › MLX Code: Explain Selection, Refactor Selection, Generate Tests, Fix Issues, Ask MLX Code. The extension hands the request to the app through the shared App Group container and the mlxcode:// URL scheme; the app answers with whichever AI backend is selected.

Desktop widget (WidgetKit)

Small, medium and large sizes showing model status, token speed, memory use and quick-action deep links.

GitHub integration

View and create issues, list and create PRs, manage branches, scan for exposed credentials before pushing.

User memories

50+ built-in coding standards across 8 categories injected into the system prompt. Custom memories stay local.

Context management

  • Token budgeting: 70% messages, 20% project context, 10% summary
  • Automatic message compaction when the context fills up; message summaries come from the AI backend and fall back to the message's first sentence when AI is off or unavailable
  • Live context-window usage bar
  • Project context auto-included when a workspace is open

Syntax highlighting

Swift, Python, JavaScript, TypeScript, Bash, JSON and Objective-C.


AI backends (NovaAIKit)

MLX Code uses NovaAIKit 1.0.0 (products NovaAIKit and NovaAIKitMLX), the same AI layer as every other Jordan Koch macOS app. Settings › AI is NovaAIKit's settings view:

Setting What it does
Use AI On/off. Off = nothing is sent to any model.
Backend Auto, Ollama, MLX (Apple Silicon), TinyLLM, TinyChat, OpenWebUI, Nova Gateway / Fleet, OpenRouter, OpenAI
Model / URL Per backend; models are listed from the backend, URLs are editable
API keys SecureField → macOS Keychain only; a stored key is shown as "stored", never as text
Timeout 10 s – 300 s per request
Test connection Probes the selected backend (Auto: all of them)
  • MLX (Apple Silicon) is the default and means the model loaded in MLX Code itself: the app's mlx-swift-lm engine is plugged into NovaAIKit's mlx backend (NovaAIClosureModel), so chat keeps native token streaming, tool specs and Stop. Nothing leaves the Mac.
  • Auto tries Ollama → MLX (in-process) → TinyChat → TinyLLM → OpenWebUI → Nova Gateway → OpenRouter → OpenAI, skipping servers that just failed and keyed clouds without a key. Transient errors are retried with backoff.
  • Status indicator in the chat toolbar: green = reachable (backend, host, latency), orange = offline, grey = off. Click it to re-check.

When AI is off or unavailable

Feature AI off Every backend down / reply rejected
Chat Transcript notice ("AI is off…"), nothing sent Transcript shows "⚠️ No reply: ", UI stays usable
Message summaries (context compaction) First sentence of the message First sentence of the message
AI slash commands, multi-file transform/generate Readable "AI is turned off" error The backend error
Tools, Git/GitHub, Xcode, project panels, /search, /index Work normally Work normally

Summaries are validated (non-empty, plain text, ≤ 240 characters) before use.

Keys and migration

  • Keys live only in the Keychain under com.jkoch.mlxcode.<backend> (account apiKey), and are only sent over https.
  • On first launch of 6.5.0 the v6.4 balancer's OpenRouter key (com.jordankoch.mlxcode.openrouter) is copied to com.jkoch.mlxcode.openrouter, verified, and the old item is deleted. The balancer toggles become Auto if any was on, and a custom Nova Gateway URL is kept. Any plaintext AI keys in UserDefaults are moved to the Keychain or deleted.
  • Google/Azure/AWS/IBM backends are not supported (MLX Code never shipped them).

Models

Uses mlx-community models from Hugging Face, quantized for Apple Silicon.

Model Size Context Best for
Qwen3.5 9B (default) ~5 GB 256K General coding, native tool calling (16 GB+ RAM)
Qwen3-Coder 30B-A3B (MoE) ~17 GB 256K Agentic coding (32 GB+ RAM)
Devstral Small 2 24B ~14 GB 384K Software-engineering agent tasks (32 GB+ RAM)
Qwen3.6 35B-A3B (MoE) ~20 GB 256K High quality at MoE speed (32 GB+ RAM)
Qwen3.6 27B ~16 GB 256K Best quality, slower (32 GB+ RAM)
Gemma 4 E4B ~4 GB 128K Efficient general model
Qwen3.5 2B ~1.5 GB 256K Fast edits on 8 GB Macs
Qwen 2.5 7B (legacy) ~4 GB 32K Kept for existing installs

All entries are mlx-community 4-bit repos supported by mlx-swift-lm 3.32. Long contexts need a lot of memory; use the KV-cache settings to cap or quantize them. Models download through the native Swift Hugging Face client (HubClient). Custom mlx-community models work too. Only SafeTensors weights are accepted; PyTorch pickle files are rejected.


Nova API Server

Local HTTP API on port 37422 (loopback only). POST/DELETE need the app's bearer token. Chat requests go through the same routing as the UI (selected backend, AI on/off).

Method Endpoint Description
GET /api/status App status, model state, uptime
GET /api/ping Health check
GET /api/conversations List all conversations
POST /api/chat Send message, get response
GET /api/model Current model info
POST /api/model/load Load a model
GET /api/metrics Performance metrics (tokens/sec, memory)
POST /api/cancel Cancel current generation
GET /api/prompts List prompt templates
POST /api/prompts/render Render template with variables

Requirements

  • macOS 14.0 (Sonoma) or later
  • Apple Silicon (M1, M2, M3, M4)
  • 8 GB RAM minimum (16 GB recommended) for in-process models
  • No Python required: inference and downloads are pure Swift
  • Optional: a local or LAN Ollama / TinyLLM / TinyChat / OpenWebUI server, the Nova Gateway, or an OpenRouter / OpenAI key
  • Xcode 26+ only to build from source

Installation

From DMG

  1. Get MLXCode-v6.5.0-build10.dmg (Releases, or /Volumes/Data/xcode/binaries/<date>-MLXCode-v6.5.0/).
  2. Open it and drag MLX Code into Applications.
  3. Launch it, download a model from Settings › Model (or pick another backend in Settings › AI).

"MLX Code can't be opened because the developer cannot be verified"? The build isn't Developer-ID-signed and notarized. Open it anyway: on macOS 15 and later, double-click it, dismiss the dialog, then System Settings › Privacy & Security › Open Anyway. Or: xattr -dr com.apple.quarantine "/Applications/MLX Code.app". Notarized releases open with no prompt. Maintainers: see RELEASE.md.

Enabling the Xcode extension

  1. System Settings › Privacy & Security › Extensions › Xcode Source Editor
  2. Enable MLX Code
  3. Restart Xcode, select code, then Editor › MLX Code

Building

Requires Xcode 26 or later and Apple's Metal Toolchain (MLX compiles Metal shaders):

xcodebuild -downloadComponent MetalToolchain
git clone git@github.com:kochj23/MLXCode.git
cd MLXCode
open "MLX Code.xcodeproj"   # resolves mlx-swift, mlx-swift-lm, swift-huggingface, swift-transformers, NovaAIKit

Command-line builds must pass -skipMacroValidation -skipPackagePluginValidation (mlx-swift-lm's #huggingFaceTokenizerLoader() is a Swift macro):

xcodebuild -project "MLX Code.xcodeproj" -scheme "MLX Code" -configuration Release \
  -skipMacroValidation -skipPackagePluginValidation build

Pinned dependency versions are committed in MLX Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved. Release: scripts/release.sh (Developer ID + notarization, see RELEASE.md).

A wall of CompileMetalFile … missing Metal Toolchain errors means the Metal Toolchain isn't installed.


Security

  • Local by default: the default backend is the in-process MLX model; nothing leaves the Mac unless you pick a remote backend
  • Keys in the Keychain only (com.jkoch.mlxcode.*), never in UserDefaults, files or the environment; refused over plain http; never logged
  • CommandValidator blocks dangerous shell patterns (rm -rf, fork bombs, sudo, eval, curl|sh)
  • ModelSecurityValidator enforces SafeTensors-only and verifies hashes via CryptoKit
  • Nova API binds to loopback, requires a bearer token for POST/DELETE, no wildcard CORS
  • SecureLogger with category-based logging, no PII in logs
  • RepetitionDetector stops inference loops
  • No telemetry, analytics or crash reporting

What it does not do

  • No web browsing or URL fetching
  • No image/video/audio generation
  • Small local models (3–14B) mean imperfect multi-step reasoning; tool calls from local models are sometimes malformed (JSON auto-repair helps)
  • The Xcode extension opens the app rather than answering inline

Test Suite

575 tests across 28 files: models, services, security, context budgeting, build parsing, Keychain, tool registry, JSON repair, prompt templates, source-level security scans, the inference engine (parameter mapping, Stop/cancellation, native tool calls, download patterns, catalog, context parsing) via a mock generator, and the NovaAIKit integration (AIKitAdoptionTests: unit, security, retry, performance, integration, functional and frame tests with a stub URLProtocol and in-memory Keychain; no network, no real keys, no model download).

xcodebuild -project "MLX Code.xcodeproj" -scheme "MLX Code" \
  -destination "platform=macOS" -skipMacroValidation -skipPackagePluginValidation test

Optional real-model integration test (skipped by default):

TEST_RUNNER_MLXCODE_INTEGRATION=1 TEST_RUNNER_MLXCODE_MODEL_PATH=/path/to/mlx-model \
  xcodebuild -project "MLX Code.xcodeproj" -scheme "MLX Code" -destination "platform=macOS" \
  -skipMacroValidation -only-testing:"MLX Code Tests/MLXServiceTests/testIntegrationLoadAndStreamRealModel" test

Version History

Version Date Highlights
6.5.0 2026-11-11 AI moved to NovaAIKit 1.0.0: AI on/off, 9-way backend picker with Auto failover, in-process MLX as the mlx backend, Keychain-only keys (com.jkoch.mlxcode.*), status indicator, validated summaries with built-in fallback; the v6.4 balancer, OpenRouter client and KeychainStore removed
6.4.0 2026-11-06 mlx-swift-lm 3.32 / mlx-swift 0.32.3, real Stop, native tool calling, KV-cache settings, min-p, model catalog refresh, complete tokenizer downloads; multi-model load balancer
6.3.0 2026-03-04 Native model downloads, tool-call reliability, Xcode Source Editor Extension
6.1.0 2026-02-26 Security hardening and code-quality audit
5.0.0 2026-02-19 Focused rewrite: removed media generation, RAG and auxiliary panels
4.0.0 2026-02-19 Native mlx-swift inference
1.0.0 2025-11-18 Initial release

See CHANGELOG.md for details.


License

MIT License. Copyright 2025-2026 Jordan Koch. See LICENSE.


Written by Jordan Koch (@kochj23)

About

Local AI coding assistant for macOS on Apple Silicon: in-process MLX (mlx-swift-lm) with native tool calling and Stop, 14 tools, Xcode extension, widget. AI via NovaAIKit: on/off, Auto failover across MLX, Ollama, TinyLLM, TinyChat, OpenWebUI, Nova Gateway, OpenRouter, OpenAI; Keychain-only keys.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages