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:
UsageProviderenum – The single source of truth for provider identifiers, defined inSources/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 - Provider implementation registry – Runtime logic for availability checks, settings UI, and login flows, found in
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. 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
- Declare the identifier by adding a case to
UsageProviderinSources/CodexBarCore/Providers/Providers.swift - Register metadata via
ProviderDescriptorinSources/CodexBar/Providers/Shared/ProviderDescriptorRegistry.swift - Implement runtime logic by creating a struct conforming to
ProviderImplementationwithisAvailable,settingsFields, andpresentationmethods - Connect the factory by adding your implementation to the switch in
Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift - Expose settings by extending the global
SettingsStoreand registering inSettingsStoreRegistry - Validate changes by running
make testto 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, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →