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

> Learn how to add a new AI model provider to Magnitude. Implement the Provider interface and register your solution with this step-by-step guide for developers.

- Repository: [Magnitude/magnitude](https://github.com/magnitudedev/magnitude)
- Tags: how-to-guide
- Published: 2026-09-06

---

**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`](https://github.com/magnitudedev/magnitude/blob/main/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`](https://github.com/magnitudedev/magnitude/blob/main/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`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/magnitude/provider.ts) for a complete working example.

```typescript
// 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`](https://github.com/magnitudedev/magnitude/blob/main/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`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/registry.ts) constructs the central registry. Pass your provider via the `discoverableProviders` array.

```typescript
// 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`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/catalog-aggregator.ts))
- Exposes RPC operations: `resolveModel`, `discoverModelProperties`, `listProviders`

---

## Step 4: Verify Integration

Test your provider implementation before submitting:

```typescript
// 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`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/registry.ts) | `makeProviderRegistry` and `ProviderRegistryLive` layer construction |
| [`packages/providers/src/catalog-aggregator.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/catalog-aggregator.ts) | Merges multiple provider catalogs into unified `ModelCatalog` |
| [`packages/providers/src/magnitude/provider.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/magnitude/provider.ts) | Reference implementation of a hosted provider |
| [`packages/providers/src/custom-endpoint/provider.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/providers/src/custom-endpoint/provider.ts) | Example: user-configurable HTTP endpoint provider |
| [`packages/providers/src/provider-client.ts`](https://github.com/magnitudedev/magnitude/blob/main/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`](https://github.com/magnitudedev/magnitude/blob/main/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`](https://github.com/magnitudedev/magnitude/blob/main/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`](https://github.com/magnitudedev/magnitude/blob/main/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.