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

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:

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. This value acts as the canonical identifier throughout the application.

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. Locate the registerDescriptors static block and add your provider:

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 to hold custom settings:

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 conforming to ProviderImplementation:

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 by adding a case to the factory switch:

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 (or the equivalent file containing the global store definition) and add:

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:

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:

import Foundation
import CodexBarCore

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

HelloWorldProviderImplementation.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, the switch case in ProviderImplementationRegistry.swift, and the enum case in Providers.swift. Run make test to verify the suite passes, and you can now select Hello World from the Preferences pane.

Summary

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, register a ProviderDescriptor in Sources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift, and return your implementation from the switch in 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →