# How OpenClaude Resolves Provider Profiles with Descriptor Routes

> Discover how OpenClaude resolves provider profiles using descriptor routes. Learn about mapping route IDs to compiled RouteDescriptors for runtime behavior.

- Repository: [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude)
- Tags: internals
- Published: 2026-09-02

---

**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)](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)](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.

```typescript
// 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:

```typescript
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

```typescript
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)](https://github.com/Gitlawb/openclaude/blob/main/src/services/api/openaiShim.ts) consume the descriptor to build HTTP requests:

```typescript
// 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:

```typescript
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)](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts) module uses the descriptor's `validation` and `setup` fields:

```typescript
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.authMode` against 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)](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.ts) enforce consistency across all provider integrations:

```typescript
// 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:

1. **Define the descriptor** — Add a complete `RouteDescriptor` object to the integration artifact source
2. **Regenerate artifacts** — The build process compiles descriptors into [`integrationArtifacts.generated.js`](https://github.com/Gitlawb/openclaude/blob/main/integrationArtifacts.generated.js)
3. **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

```typescript
// 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)](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
- **`RouteDescriptor` fields** drive model selection, request construction, auth handling, and capability detection
- **Validation in [[`providerValidation.ts`](https://github.com/Gitlawb/openclaude/blob/main/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`](https://github.com/Gitlawb/openclaude/blob/main/integrationArtifacts.generated.js) (loaded lazily by [[`src/integrations/registry.ts`](https://github.com/Gitlawb/openclaude/blob/main/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.