How OpenClaude Resolves Provider Profiles with Descriptor Routes
OpenClaude resolves provider profiles by mapping a user-defined route ID to a compiled RouteDescriptor through a lazy-loaded integration registry, then deriving runtime behavior from that descriptor's fields.
The OpenClaude CLI normalizes access to dozens of LLM providers—vendors like OpenAI and Anthropic, gateways like OpenRouter, and Anthropic-proxy services—through a unified descriptor-driven architecture. When you configure a provider profile, you store only a simple string identifier. The actual provider logic lives in compiled descriptors that the system resolves on demand. This design eliminates hardcoded provider logic and makes adding new backends a data-driven exercise.
What Is a Provider Profile in OpenClaude
A provider profile is a minimal user configuration that contains a route ID—for example, openai, anthropic, xai, or openrouter. This identifier acts as a lookup key into the integration registry. Profiles do not duplicate provider metadata like base URLs, authentication modes, or model catalogs. Instead, they defer to the centralized descriptor system, ensuring that updates to provider capabilities propagate automatically.
The Integration Registry: Lazy Loading and Resolution
The resolution pipeline begins with the integration registry, implemented across two core files: [src/integrations/registry.ts](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/registry.ts) and [src/integrations/routeMetadata.ts](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/routeMetadata.ts).
Step 1: Ensure Registry Is Loaded
The first call to any descriptor helper triggers ensureIntegrationsLoaded(). This function lazily imports the generated artifact file that contains all compiled descriptors for vendors, gateways, and Anthropic proxies.
// Called automatically by descriptor lookup functions
import { ensureIntegrationsLoaded } from './integrations/registry';
await ensureIntegrationsLoaded(); // One-time initialization
Step 2: Resolve the Route Descriptor
The central resolution function getRouteDescriptor(routeId) queries three internal maps to find a match:
import { getRouteDescriptor } from './integrations/routeMetadata';
const profileRouteId = 'xai';
const descriptor = getRouteDescriptor(profileRouteId);
if (!descriptor) {
throw new Error(`Unknown provider route: ${profileRouteId}`);
}
// descriptor contains: label, defaultBaseUrl, capabilities, validation, setup, etc.
console.log(descriptor.label); // "xAI"
console.log(descriptor.defaultBaseUrl); // "https://api.x.ai/v1"
The function searches across:
- Vendors — direct provider integrations (OpenAI, Anthropic, Google, etc.)
- Gateways — unified routing services (OpenRouter, LiteLLM proxy, etc.)
- Anthropic proxies — services that expose an Anthropic-compatible interface
How Descriptor Fields Drive Runtime Behavior
Once resolved, the RouteDescriptor provides structured data for every operational concern:
Model Selection and Defaults
import { getRouteDefaultModel } from './integrations/routeMetadata';
const defaultModel = getRouteDefaultModel('openai'); // → 'gpt-4o-mini'
The descriptor's models array defines available models with capability flags, context windows, and pricing metadata.
Request Construction
Services like the OpenAI shim in [src/services/api/openaiShim.ts](https://github.com/Gitlawb/openclaude/blob/main/src/services/api/openaiShim.ts) consume the descriptor to build HTTP requests:
// Inside api service layer
const descriptor = getRouteDescriptor(runtimeShimContext.routeId);
const baseUrl = descriptor.defaultBaseUrl;
const path = descriptor.transportConfig?.endpointPath ?? '/v1/chat/completions';
// Auth header construction from setup.authMode
const authHeader = descriptor.setup?.authMode === 'api-key'
? `Bearer ${apiKey}`
: descriptor.setup?.authMode === 'oauth2'
? `Bearer ${accessToken}`
: undefined;
Capability Detection
The descriptor's capabilities object enables conditional logic for features like vision, function calling, and streaming:
if (descriptor.capabilities?.supportsVision) {
// Attach image content to message payload
}
if (descriptor.capabilities?.supportsStreaming) {
// Enable SSE response handling
}
Provider Profile Validation
Before activating a profile, OpenClaude validates that required credentials are present. The [src/utils/providerValidation.ts](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts) module uses the descriptor's validation and setup fields:
import { validateProviderProfile } from './utils/providerValidation';
try {
await validateProviderProfile('anthropic');
// Profile is ready to use
} catch (error) {
// Error describes missing environment variables or configuration
console.error(error.message); // e.g., "ANTHROPIC_API_KEY environment variable required"
}
The validation logic inspects:
- Required environment variables — based on
validation.requiredEnvVars - Auth mode compatibility — matching
setup.authModeagainst available credentials - Base URL reachability — optional connectivity checks for custom endpoints
The Descriptor Type System
The core type definitions in [src/integrations/descriptors.ts](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.ts) enforce consistency across all provider integrations:
// Core descriptor structure (simplified)
interface RouteDescriptor {
id: string; // route ID used in profiles
label: string; // display name
category: 'vendor' | 'gateway' | 'anthropic-proxy';
defaultBaseUrl: string;
transportConfig: {
kind: 'openai' | 'anthropic' | 'google' | 'custom';
endpointPath?: string;
};
setup: {
authMode: 'api-key' | 'oauth2' | 'none';
docsUrl?: string;
};
validation: {
requiredEnvVars: string[];
};
capabilities: {
supportsStreaming: boolean;
supportsVision: boolean;
supportsFunctionCalling: boolean;
maxContextTokens?: number;
};
models: ModelDescriptor[];
}
This schema guarantees that every component—CLI commands, API services, and UI panels—receives predictable, type-safe provider metadata.
Adding a New Provider: Data-Driven Integration
The descriptor architecture enables new provider support without code changes to consuming services. To add a provider:
- Define the descriptor — Add a complete
RouteDescriptorobject to the integration artifact source - Regenerate artifacts — The build process compiles descriptors into
integrationArtifacts.generated.js - Profile resolution works automatically — Users can immediately create profiles with the new route ID
No changes are required in getRouteDescriptor, request builders, or validation logic. The unified lookup table centralizes all provider knowledge.
Complete Resolution Flow Example
// Full pipeline: profile → descriptor → validated request config
import { getRouteDescriptor, getRouteDefaultModel } from './integrations/routeMetadata';
import { validateProviderProfile } from './utils/providerValidation';
async function prepareProviderConfig(profileRouteId: string) {
// 1. Resolve descriptor
const descriptor = getRouteDescriptor(profileRouteId);
if (!descriptor) {
throw new Error(`Unknown provider: ${profileRouteId}`);
}
// 2. Validate credentials
await validateProviderProfile(profileRouteId);
// 3. Build runtime configuration
return {
baseUrl: descriptor.defaultBaseUrl,
model: getRouteDefaultModel(profileRouteId),
authMode: descriptor.setup.authMode,
streaming: descriptor.capabilities.supportsStreaming,
vision: descriptor.capabilities.supportsVision,
transportKind: descriptor.transportConfig.kind,
};
}
// Usage
const config = await prepareProviderConfig('openrouter');
// config.baseUrl → "https://openrouter.ai/api/v1"
// config.model → "openai/gpt-4o-mini"
// config.transportKind → "openai" (OpenRouter uses OpenAI-compatible transport)
Summary
- Provider profiles store only route IDs — minimal configuration that resolves to rich descriptors
getRouteDescriptor()in [src/integrations/routeMetadata.ts](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/routeMetadata.ts) is the single entry point for profile resolution- Lazy loading via
ensureIntegrationsLoaded()defers descriptor initialization until first use - Three descriptor categories — vendors, gateways, and Anthropic proxies — share a unified interface
RouteDescriptorfields drive model selection, request construction, auth handling, and capability detection- Validation in [
providerValidation.ts](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts) ensures profiles are usable before activation - Data-driven design enables new providers through descriptor addition alone, without service-layer changes
Frequently Asked Questions
What happens if a provider profile references an unknown route ID?
getRouteDescriptor() returns null for unrecognized route IDs. Calling code typically throws an error or prompts the user to select from available providers. The registry's three internal maps (vendors, gateways, Anthropic proxies) collectively define the valid route ID namespace.
Can users override descriptor defaults in their profiles?
The core descriptor system does not support per-profile overrides of base URLs or capabilities. However, the profile storage layer can extend the minimal route ID with additional fields. Service layers that consume descriptors may merge profile-specific settings—such as custom base URLs—onto the resolved descriptor values before request construction.
How does OpenClaude handle provider API changes?
Descriptor updates propagate automatically when the integration artifacts are regenerated. The CLI ships with compiled descriptors, so updating to a new OpenClaude version refreshes the entire provider catalog. No manual profile migration is required since profiles contain only stable route IDs.
Where are the actual descriptor definitions maintained?
Descriptor source data lives in the build-time integration artifact generator. The runtime consumes compiled JavaScript from integrationArtifacts.generated.js (loaded lazily by [src/integrations/registry.ts](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/registry.ts)). This compilation step enables type-safe, tree-shakeable provider definitions while maintaining a clean runtime API.
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 →