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
ProviderModelentries 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
idto its implementation - Aggregates its catalog via
makeAggregatedCatalog(defined inpackages/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
Providerinterface from@magnitudedev/aiwithid,displayName,catalog, andbindModel - Use Effect for all async operations to maintain composability with the rest of the Magnitude stack
- Register via
ProviderRegistryLiveby adding your provider todiscoverableProviders - Test your catalog and binding logic independently before integration
- Reference
packages/providers/src/magnitude/provider.tsfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →