# What Is the Descriptor-First Provider System in OpenClaude?

> Explore OpenClaude's descriptor-first provider system. Add new AI vendors easily by replacing hard-coded logic with declarative metadata, avoiding code modifications.

- Repository: [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude)
- Tags: deep-dive
- Published: 2026-09-05

---

**OpenClaude's descriptor-first provider system replaces hard-coded provider logic with declarative metadata objects stored in `src/integrations/`, enabling new AI vendors to be added without modifying routing or transport code.**

The descriptor-first architecture in **OpenClaude** (Gitlawb/openclaude) centralizes all provider, model, gateway, and brand metadata into type-safe descriptor files. This design transforms provider management from scattered conditional logic into a data-driven pipeline where metadata—not code—determines available routes and capabilities.

## How the Descriptor System Works

At its core, the system treats every integration as a **descriptor object** defined using helper functions in [`src/integrations/define.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/define.ts). These descriptors capture everything needed to route and shape requests: authentication modes, base URLs, transport protocols, and model catalogs.

The architecture operates through five coordinated layers:

| Layer | Key File | Responsibility |
|-------|----------|----------------|
| **Type definitions** | [`src/integrations/descriptors.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.ts) | TypeScript interfaces for all descriptor shapes |
| **Registration** | [`src/integrations/index.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/index.ts) + [`registry.ts`](https://github.com/Gitlawb/openclaude/blob/main/registry.ts) | Loads generated manifest into in-memory registry |
| **Route resolution** | [`src/integrations/routeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/routeMetadata.ts) | Maps user config/env vars to concrete descriptor IDs |
| **Runtime metadata** | [`src/integrations/runtimeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/runtimeMetadata.ts) | Derives request-shaping flags from active descriptor |
| **Compatibility bridges** | [`compatibility.ts`](https://github.com/Gitlawb/openclaude/blob/main/compatibility.ts), [`providerFlag.ts`](https://github.com/Gitlawb/openclaude/blob/main/providerFlag.ts) | Preserves legacy env-based API contracts |

According to the [Integrations Architecture doc](https://github.com/Gitlawb/openclaude/blob/main/docs/architecture/integrations.md), *"the primary source of truth now lives in these layers"* and *"descriptor metadata should decide which route exists and what it supports; runtime code should execute that metadata, not replace it with a parallel provider matrix."*

## Anatomy of a Descriptor File

Descriptors follow a consistent pattern using `define*` helpers. Here is a complete gateway descriptor from [`src/integrations/gateways/acme.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/gateways/acme.ts):

```ts
import { defineGateway, defineCatalog } from '../define.js';

const catalog = defineCatalog({
  source: 'static',
  models: [
    {
      id: 'acme-fast',
      apiName: 'acme/fast',
      modelDescriptorId: 'acme-fast',
    },
  ],
});

export default defineGateway({
  id: 'acme',
  label: 'Acme AI',
  category: 'hosted',
  defaultBaseUrl: 'https://api.acme.example/v1',
  defaultModel: 'acme/fast',
  setup: {
    requiresAuth: true,
    authMode: 'api-key',
    credentialEnvVars: ['ACME_API_KEY'],
  },
  transportConfig: {
    kind: 'openai-compatible',
    openaiShim: {
      supportsApiFormatSelection: false,
      supportsAuthHeaders: true,
    },
  },
  catalog,
});

```

Key fields include:

- **`transportConfig.kind`** — the routing contract (e.g., `'openai-compatible'`)
- **`setup`** — authentication requirements and environment variable names
- **`catalog`** — model-specific entries with their own capability flags

## The Descriptor Lifecycle at Runtime

The descriptor-first system processes providers through a four-phase pipeline:

### 1. Generation

The `bun run integrations:generate` command scans all descriptor files and compiles [`src/integrations/generated/integrationArtifacts.generated.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/generated/integrationArtifacts.generated.ts). This artifact contains the complete, type-safe manifest of all integrations.

### 2. Loading

[`src/integrations/index.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/index.ts) imports the generated artifact and registers each descriptor via [`registry.ts`](https://github.com/Gitlawb/openclaude/blob/main/registry.ts), exposing typed accessors like `getVendor()`, `getGateway()`, and `getModel()`.

### 3. Route Selection

[`routeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/routeMetadata.ts) evaluates:

- Environment variables (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)
- Saved user profiles
- CLI flags like `--provider`

The result is a concrete **descriptor ID** identifying the active integration.

### 4. Request Shaping

[`runtimeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/runtimeMetadata.ts) merges the selected descriptor with the chosen model's catalog entry, producing flags consumed by the OpenAI-shim transport layer in `src/services/api/openaiShim/`:

- `supportsApiFormatSelection`
- `supportsAuthHeaders`
- `supportsVision`
- Other capability-derived settings

## Adding a New Provider: Complete Contributor Workflow

The descriptor-first system enables zero-code integration additions. To add a new vendor:

**Step 1:** Create a descriptor file at [`src/integrations/vendors/myco.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/vendors/myco.ts):

```ts
import { defineVendor } from '../define.js';

export default defineVendor({
  id: 'myco',
  label: 'MyCo AI',
  category: 'hosted',
  defaultBaseUrl: 'https://api.myco.com/v1',
  setup: {
    requiresAuth: true,
    authMode: 'api-key',
    credentialEnvVars: ['MYCO_API_KEY'],
  },
});

```

**Step 2:** Regenerate the integration manifest:

```bash
bun run integrations:generate

```

**Step 3:** The vendor automatically appears in CLI commands:

```bash
openclaude provider list
openclaude --provider=myco chat

```

No changes to routing logic, transport code, or registry files are required.

## Using Descriptor Metadata in Application Code

Runtime code accesses descriptor-derived capabilities through utility functions. For model-specific feature detection:

```ts
import { getModelDescriptor } from '../../utils/model/providers';

const descriptor = getModelDescriptor('gpt-4o-mini');

if (descriptor?.capabilities?.supportsVision) {
  request.images = [...attachments];
}

```

The `capabilities` object originates directly from the model's descriptor entry (e.g., [`src/integrations/models/gpt.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/models/gpt.ts)).

For provider enumeration in CLI interfaces:

```ts
// From src/commands/provider/provider.test.tsx
import { useProviders } from '../../utils/providerFlag';

const providers = useProviders(); // Returns descriptor-generated list

```

## Legacy Compatibility Bridges

While descriptors are the source of truth, narrow compatibility layers preserve existing workflows:

- **[`src/integrations/compatibility.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/compatibility.ts)** — maps historic preset names to descriptor route IDs
- **[`src/utils/providerFlag.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerFlag.ts)** — maintains legacy `--provider` env variable writes
- **[`src/utils/model/providers.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/model/providers.ts)** — preserves `APIProvider` / `LegacyAPIProvider` types for older callers

These bridges are intentionally minimal. New features should extend descriptors rather than compatibility code.

## Key Source Files

| File | Purpose |
|------|---------|
| [[`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) | Core type definitions for all descriptor variants |
| [[`src/integrations/define.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/define.ts)](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/define.ts) | Helper functions (`defineVendor`, `defineGateway`, `defineModel`) |
| [[`src/integrations/index.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/index.ts)](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/index.ts) | Loader for generated integration artifacts |
| [[`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) | In-memory registry with typed accessors |
| [[`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) | Route resolution from config and environment |
| [[`src/integrations/runtimeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/runtimeMetadata.ts)](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/runtimeMetadata.ts) | Request-shaping flag derivation |
| [[`docs/architecture/integrations.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/architecture/integrations.md)](https://github.com/Gitlawb/openclaude/blob/main/docs/architecture/integrations.md) | Architectural design documentation |
| [[`docs/integrations/overview.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/integrations/overview.md)](https://github.com/Gitlawb/openclaude/blob/main/docs/integrations/overview.md) | Contributor guide for descriptor authoring |

## Summary

- **Descriptor-first architecture** centralizes provider metadata in `src/integrations/` instead of scattering logic across the codebase
- **Five-layer pipeline** (definitions → generation → registration → routing → runtime) converts declarative descriptors into executable request configuration
- **Zero-code provider addition** is possible: create a descriptor file, run `bun run integrations:generate`, and the integration appears automatically
- **Type safety** is enforced through [`descriptors.ts`](https://github.com/Gitlawb/openclaude/blob/main/descriptors.ts) interfaces and generated artifacts
- **Legacy compatibility** is maintained through narrow bridge layers, with new functionality directed toward descriptors

## Frequently Asked Questions

### What is a descriptor in OpenClaude?

A **descriptor** is a declarative metadata object—created with helpers like `defineVendor()`, `defineGateway()`, or `defineModel()`—that fully specifies an AI provider's identity, authentication requirements, transport protocol, and available models. Descriptors live as individual files under `src/integrations/` and serve as the single source of truth for provider behavior.

### How do I add a new AI provider to OpenClaude?

Create a new descriptor file in the appropriate `src/integrations/` subdirectory (e.g., `vendors/`, `gateways/`), define the provider using the relevant `define*` helper, then run `bun run integrations:generate`. The provider automatically becomes available in CLI commands and API routes without modifying any transport or routing code.

### Where does OpenClaude store provider authentication settings?

Authentication requirements are declared in each descriptor's `setup` field, which specifies `authMode` (e.g., `'api-key'`), `credentialEnvVars` array, and whether auth is required. Runtime code reads these values from [`routeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/routeMetadata.ts) and [`runtimeMetadata.ts`](https://github.com/Gitlawb/openclaude/blob/main/runtimeMetadata.ts) to configure headers and connection parameters.

### Can I use descriptor metadata to check model capabilities at runtime?

Yes. Import `getModelDescriptor()` from `src/utils/model/providers` to retrieve the full descriptor for any model ID. The returned object's `capabilities` field contains boolean flags like `supportsVision`, `supportsToolCalling`, and `supportsApiFormatSelection` that originated from the model's catalog entry in its descriptor file.