# How to Add a New AI Provider to CodexBar: A Complete Implementation Guide

> Learn how to add a new AI provider to CodexBar. This guide covers enum declaration, provider registration, and implementation for runtime logic and UI.

- Repository: [Peter Steinberger/CodexBar](https://github.com/steipete/CodexBar)
- Tags: how-to-guide
- Published: 2026-07-07

---

**To add a new AI provider to CodexBar, you must declare a new case in the `UsageProvider` enum, register a `ProviderDescriptor` with metadata and fetch plans, and implement the `ProviderImplementation` protocol to handle runtime logic, settings UI, and availability checks.**

Adding a new AI provider to CodexBar requires touching three distinct registry layers in the steipete/CodexBar repository. The architecture cleanly separates static metadata from runtime behavior, enabling you to integrate custom AI services with full support for the menu bar UI, CLI, and preferences window. This guide provides the exact file paths, method signatures, and code patterns needed to register a new provider.

## Understanding the Provider Architecture

CodexBar uses a three-layer registration system to manage AI providers. Each layer serves a specific purpose and corresponds to a specific source file:

- **`UsageProvider` enum** – The single source of truth for provider identifiers, defined in [`Sources/CodexBarCore/Providers/Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/Providers.swift)
- **Provider descriptor registry** – Static metadata including display names, CLI names, and fetch plans, located in [`Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift)
- **Provider implementation registry** – Runtime logic for availability checks, settings UI, and login flows, found in [`Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift)

To successfully add a new AI provider, you must update all three layers.

## Step 1: Declare the Provider Identifier

First, add a new case to the `UsageProvider` enum in [`Sources/CodexBarCore/Providers/Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/Providers.swift). This value acts as the canonical identifier throughout the application.

```swift
public enum UsageProvider: String, CaseIterable, Sendable, Codable {
    // ... existing cases like openai, anthropic ...
    case myai               // <-- add your new provider here
}

```

This enum is the single source of truth for provider IDs; every other component indexes by this value.

## Step 2: Register the Provider Descriptor

Next, register static metadata by creating a `ProviderDescriptor` entry in [`Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift). Locate the `registerDescriptors` static block and add your provider:

```swift
private static let registerDescriptors: Void = {
    // ... existing registrations ...
    _ = ProviderDescriptorRegistry.register(
        ProviderDescriptor(
            id: .myai,
            metadata: ProviderMetadata(
                id: .myai,
                displayName: "My AI",
                sessionLabel: "My AI",
                weeklyLabel: "My AI weekly",
                opusLabel: nil,
                supportsOpus: false,
                supportsCredits: true,
                creditsHint: "Credit-based pricing",
                toggleTitle: "Enable My AI",
                cliName: "myai",
                defaultEnabled: false,
                isPrimaryProvider: false,
                usesAccountFallback: false,
                browserCookieOrder: nil,
                dashboardURL: "https://myai.example.com/dashboard",
                subscriptionDashboardURL: nil,
                changelogURL: "https://myai.example.com/changelog",
                statusPageURL: nil,
                statusLinkURL: nil,
                statusWorkspaceProductID: nil
            ),
            fetchPlan: ProviderFetchPlan(
                sourceModes: [.api],
                fetchOutcome: { context in
                    // Return async fetch result with usage/quotas
                    // Implementation depends on your API
                }
            )
        )
    )
}()

```

The `displayName`, `cliName`, and `dashboardURL` fields appear in the UI and command-line interface. The `fetchPlan` tells CodexBar how to obtain usage data, whether via API, web scraping, or other methods.

## Step 3: Implement the Provider Runtime

Create a new directory under `Sources/CodexBar/Providers/MyAI/` and add two files: a settings store and the implementation struct.

First, create [`MyAISettingsStore.swift`](https://github.com/steipete/CodexBar/blob/main/MyAISettingsStore.swift) to hold custom settings:

```swift
import Foundation
import CodexBarCore

final class MyAISettingsStore: SettingsStore {
    @Published var apiKey: String = ""
    @Published var endpoint: String = "https://api.myai.example.com"
}

```

Then create [`MyAIProviderImplementation.swift`](https://github.com/steipete/CodexBar/blob/main/MyAIProviderImplementation.swift) conforming to `ProviderImplementation`:

```swift
import AppKit
import CodexBarCore
import Foundation

struct MyAIProviderImplementation: ProviderImplementation {
    let id: UsageProvider = .myai

    @MainActor
    func presentation(context _: ProviderPresentationContext) -> ProviderPresentation {
        ProviderPresentation { _ in "myai" }
    }

    @MainActor
    func observeSettings(_ settings: SettingsStore) {
        // Force loading of secure fields so the UI displays them
        _ = settings.myAISettings.apiKey
    }

    @MainActor
    func isAvailable(context: ProviderAvailabilityContext) -> Bool {
        // Check environment variable or stored credentials
        if ProcessInfo.processInfo.environment["MYAI_API_KEY"] != nil { return true }
        return !context.settings.myAISettings.apiKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }

    @MainActor
    func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] {
        [
            ProviderSettingsFieldDescriptor(
                id: "myai-api-key",
                title: "API key",
                subtitle: "Stored in ~/.codexbar/config.json or via MYAI_API_KEY env var",
                kind: .secure,
                placeholder: "sk-...",
                binding: context.stringBinding(\.myAISettings.apiKey),
                actions: [], isVisible: nil, onActivate: nil
            )
        ]
    }
}

```

The `presentation` method provides the short label for the status menu. The `isAvailable` method determines whether the provider appears enabled in the UI, while `settingsFields` constructs the preferences interface.

## Step 4: Wire Up the Implementation Factory

Register your implementation in [`Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift) by adding a case to the factory switch:

```swift
private static func makeImplementation(for provider: UsageProvider) -> (any ProviderImplementation) {
    switch provider {
    // ... existing cases ...
    case .myai: MyAIProviderImplementation()
    }
}

```

This switch statement maps each `UsageProvider` enum case to its concrete runtime class.

## Step 5: Expose Custom Settings Storage

If you created a custom `MyAISettingsStore`, expose it through the global `SettingsStore` extension. Open [`Sources/CodexBar/Providers/Shared/ProviderSettingsDescriptors.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderSettingsDescriptors.swift) (or the equivalent file containing the global store definition) and add:

```swift
extension SettingsStore {
    var myAISettings: MyAISettingsStore { get }
}

```

You must also register the store in `SettingsStoreRegistry`. Follow the pattern used for existing stores like `OpenAIAPISettingsStore` to ensure proper initialization and persistence.

## Step 6: Validate with Tests

CodexBar includes comprehensive unit tests for descriptor lists, UI rendering, and fetch pipelines. After adding your provider, run:

```bash
make test

```

All existing tests should pass. If you added custom fetch logic or new behavior, add corresponding tests in `Tests/CodexBarTests/` to maintain coverage.

## Minimal Working Example

Below is a complete minimal provider you can copy into a fresh branch. Create the directory `Sources/CodexBar/Providers/HelloWorld/` and add these files:

**HelloWorldSettingsStore.swift:**

```swift
import Foundation
import CodexBarCore

final class HelloWorldSettingsStore: SettingsStore {
    @Published var secret: String = ""
}

```

**HelloWorldProviderImplementation.swift:**

```swift
import AppKit
import CodexBarCore
import Foundation

struct HelloWorldProviderImplementation: ProviderImplementation {
    let id: UsageProvider = .helloworld   // Add .helloworld to UsageProvider first

    @MainActor
    func presentation(context _: ProviderPresentationContext) -> ProviderPresentation {
        ProviderPresentation { _ in "hello" }
    }

    @MainActor
    func observeSettings(_ settings: SettingsStore) {
        _ = settings.helloWorldSettings.secret
    }

    @MainActor
    func isAvailable(context: ProviderAvailabilityContext) -> Bool {
        true // Always available for demo purposes
    }

    @MainActor
    func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] {
        [
            ProviderSettingsFieldDescriptor(
                id: "helloworld-secret",
                title: "Secret token",
                subtitle: "Demo-only; stored locally",
                kind: .secure,
                placeholder: "secret-...",
                binding: context.stringBinding(\.helloWorldSettings.secret),
                actions: [], isVisible: nil, onActivate: nil)
        ]
    }
}

```

Add the descriptor entry in [`ProviderDescriptorRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/ProviderDescriptorRegistry.swift), the switch case in [`ProviderImplementationRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/ProviderImplementationRegistry.swift), and the enum case in [`Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Providers.swift). Run `make test` to verify the suite passes, and you can now select *Hello World* from the Preferences pane.

## Summary

- **Declare the identifier** by adding a case to `UsageProvider` in [`Sources/CodexBarCore/Providers/Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/Providers.swift)
- **Register metadata** via `ProviderDescriptor` in [`Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift)
- **Implement runtime logic** by creating a struct conforming to `ProviderImplementation` with `isAvailable`, `settingsFields`, and `presentation` methods
- **Connect the factory** by adding your implementation to the switch in [`Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift)
- **Expose settings** by extending the global `SettingsStore` and registering in `SettingsStoreRegistry`
- **Validate changes** by running `make test` to ensure no regressions

Following these steps integrates your AI service into CodexBar's menu bar, CLI, and preferences window, making it a first-class citizen alongside built-in providers.

## Frequently Asked Questions

### What is the minimum code required to add a new AI provider to CodexBar?

You must modify three files minimum: add a case to `UsageProvider` in [`Sources/CodexBarCore/Providers/Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/Providers.swift), register a `ProviderDescriptor` in [`Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift), and return your implementation from the switch in [`Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift). While you can reuse existing settings stores, creating a custom `ProviderImplementation` is mandatory for runtime logic.

### How does CodexBar determine if a provider is available?

Availability is determined by the `isAvailable(context:)` method in your `ProviderImplementation` conformance. You can check for environment variables like `MYAI_API_KEY` or validate that required credentials exist in the settings store, returning `true` only when the provider can successfully fetch data.

### Where do I store API keys for my custom AI provider?

API keys and other secrets should be stored in a custom `SettingsStore` subclass (e.g., `MyAISettingsStore`) and exposed through the global `SettingsStore` extension. The framework automatically persists these values to `~/.codexbar/config.json` when you use secure bindings in `settingsFields(context:)`.

### Do I need to modify the core library to add a provider?

Yes, you must edit files in both `CodexBarCore` (the enum definition) and `CodexBar` (the registries). However, you typically add new provider source files under `Sources/CodexBar/Providers/YourProvider/` without modifying existing provider implementations, keeping your changes isolated to the extension points.