How to Add a New AI Model Provider to Magnitude: A Step-by-Step Implementation Guide

To add a new AI model provider to Magnitude, implement the Provider interface from @magnitudedev/ai, then register your implementation via ProviderRegistryLive in packages/providers/src/registry.ts.

Magnitude's provider architecture treats every AI service as a pluggable component. Whether you're integrating OpenAI, Anthropic, or a custom internal endpoint, the same extension points apply. This guide walks through the exact files, interfaces, and registration patterns used in the Magnitude codebase.


Understanding the Provider Architecture

Magnitude decouples model discovery from execution through three core abstractions in @magnitudedev/ai:

  • Provider – The entry point that exposes a catalog of models and factory methods to bind them
  • ModelCatalog – A list of ProviderModel entries describing available models and their capabilities
  • BoundModel – A runtime handle that executes calls against a specific model instance

The ProviderRegistry in packages/providers/src/registry.ts aggregates these catalogs and surfaces unified RPC operations for the client layer.


Step 1: Implement the Provider Interface

Create a new file under packages/providers/src/<your-provider>/provider.ts. Your implementation must satisfy the Provider type exported by @magnitudedev/ai.

Required properties and methods:

Member Type Description
id ProviderId Unique string identifier (e.g., "anthropic", "openai-custom")
displayName string Human-readable name for UI rendering
catalog Effect<ModelCatalog> Effect that resolves to available models
bindModel (id, options) => Effect<BoundModel> Factory for creating callable model instances
discoverModelProperties (request) => Effect<PropertyDiscoveryResult> Optional: inspect model capabilities at runtime

Reference the Magnitude provider implementation in packages/providers/src/magnitude/provider.ts for a complete working example.

// packages/providers/src/anthropic/provider.ts
import type {
  Provider,
  ProviderModel,
  BoundModel,
  BaseCallOptions,
  ProviderModelBindOptions,
  ProviderId,
  ProviderModelId,
  ModelCatalog,
} from "@magnitudedev/ai"
import * as Effect from "effect"

export const anthropicProvider: Provider<ProviderModel, never> = {
  id: "anthropic" as ProviderId,
  displayName: "Anthropic",

  catalog: Effect.succeed([
    {
      id: "claude-3-5-sonnet-latest" as ProviderModelId,
      name: "Claude 3.5 Sonnet",
      maxTokens: 8192,
    },
    {
      id: "claude-3-opus-latest" as ProviderModelId,
      name: "Claude 3 Opus",
      maxTokens: 4096,
    },
  ]),

  bindModel: (modelId: ProviderModelId, _options: ProviderModelBindOptions) =>
    Effect.succeed({
      call: (prompt: string, callOpts: BaseCallOptions) => {
        // Implementation: POST to https://api.anthropic.com/v1/messages
        return Effect.gen(function* (_) {
          const response = yield* Effect.tryPromise({
            try: () => fetch("https://api.anthropic.com/v1/messages", {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
                "x-api-key": process.env.ANTHROPIC_API_KEY!,
              },
              body: JSON.stringify({
                model: modelId,
                max_tokens: callOpts.maxTokens ?? 4096,
                messages: [{ role: "user", content: prompt }],
              }),
            }).then(r => r.json()),
            catch: (error) => new Error(`Anthropic API error: ${error}`),
          })
          return response.content[0].text
        })
      },
      modelInfo: {
        id: modelId,
        name: modelId,
        maxTokens: 8192,
      },
    } as BoundModel<BaseCallOptions, never, never>),

  discoverModelProperties: (_request) =>
    Effect.fail(new Error("Property discovery not implemented")),
}

export default anthropicProvider

Step 2: Handle Authentication (Optional)

If your provider requires API keys or OAuth, expose an authentication field that conforms to the AuthStatus union type:

  • "authenticated" – Valid credentials configured and verified
  • "no_auth_required" – Public endpoint or no authentication needed
  • "not_configured" – Missing or invalid credentials

The ProviderRegistry polls this status and surfaces it in the client UI. See packages/providers/src/magnitude/provider.ts for credential validation patterns using Effect.


Step 3: Register Your Provider

The makeProviderRegistry function in packages/providers/src/registry.ts constructs the central registry. Pass your provider via the discoverableProviders array.

// packages/client-common/src/boot.ts (or your bootstrap entry point)
import { ProviderRegistryLive } from "@magnitudedev/providers"
import anthropicProvider from "@magnitudedev/providers/src/anthropic/provider"

export const appLayer = ProviderRegistryLive({
  magnitude: null,                     // retain built-in Magnitude provider
  discoverableProviders: [anthropicProvider],
})

The ProviderRegistryLive layer automatically:

  • Maps your provider's id to its implementation
  • Aggregates its catalog via makeAggregatedCatalog (defined in packages/providers/src/catalog-aggregator.ts)
  • Exposes RPC operations: resolveModel, discoverModelProperties, listProviders

Step 4: Verify Integration

Test your provider implementation before submitting:

// packages/providers/src/anthropic/provider.test.ts
import { Effect } from "effect"
import { describe, it, expect } from "@effect/vitest"
import anthropicProvider from "./provider"

describe("Anthropic Provider", () => {
  it("lists models in catalog", async () => {
    const catalog = await Effect.runPromise(anthropicProvider.catalog)
    expect(catalog.some(m => m.id === "claude-3-5-sonnet-latest")).toBe(true)
  })

  it("binds a model that can be called", async () => {
    const bound = await Effect.runPromise(
      anthropicProvider.bindModel("claude-3-5-sonnet-latest" as any, {})
    )
    expect(bound.call).toBeDefined()
  })
})

Run tests with pnpm test --filter @magnitudedev/providers.


Key Implementation Files

File Purpose
packages/providers/src/registry.ts makeProviderRegistry and ProviderRegistryLive layer construction
packages/providers/src/catalog-aggregator.ts Merges multiple provider catalogs into unified ModelCatalog
packages/providers/src/magnitude/provider.ts Reference implementation of a hosted provider
packages/providers/src/custom-endpoint/provider.ts Example: user-configurable HTTP endpoint provider
packages/providers/src/provider-client.ts Base utilities for providers that call remote services

Summary

  • Implement the Provider interface from @magnitudedev/ai with id, displayName, catalog, and bindModel
  • Use Effect for all async operations to maintain composability with the rest of the Magnitude stack
  • Register via ProviderRegistryLive by adding your provider to discoverableProviders
  • Test your catalog and binding logic independently before integration
  • Reference packages/providers/src/magnitude/provider.ts for production patterns including error handling and streaming responses

Frequently Asked Questions

What interface must I implement to add a new AI model provider to Magnitude?

You must implement the Provider interface exported by @magnitudedev/ai. This interface requires id, displayName, catalog, bindModel, and optionally discoverModelProperties. The catalog property returns an Effect containing a ModelCatalog, and bindModel returns a BoundModel capable of executing API calls.

Where do I register my custom provider in the Magnitude codebase?

Register your provider in packages/providers/src/registry.ts using the ProviderRegistryLive layer constructor. Pass your provider instance in the discoverableProviders array. This registration pattern is implemented in client bootstrap files like packages/client-common/src/boot.ts.

Can I add a provider that requires API key authentication?

Yes. Expose an authentication field on your provider that returns an Effect<AuthStatus>. Return "authenticated" when credentials are valid, "not_configured" when missing, or "no_auth_required" for public endpoints. The ProviderRegistry automatically surfaces this status to the client UI.

Do I need to modify the SDK package to add a new provider?

No. The SDK (packages/sdk) consumes providers through the ProviderRegistry at runtime. As long as your provider is registered in ProviderRegistryLive and its catalog resolves correctly, the SDK will discover and expose its models without code changes.

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 →