# What Is the Descriptor-First Architecture in OpenClaude?

> Discover OpenClaude's descriptor-first architecture. Centralize model and provider configurations into declarative metadata objects for efficient routing, capability checks, and validation.

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

---

**OpenClaude implements a descriptor-first architecture that centralizes all model and provider configuration into declarative metadata objects, making them the single source of truth for routing, capability checks, and validation throughout the CLI.**

Unlike traditional CLI tools that scatter provider logic across hard-coded conditionals, the `Gitlawb/openclaude` framework defines every supported model through a structured *descriptor*. This metadata-driven approach allows the entire runtime to behave generically: the code looks up the descriptor first, then adapts its behavior based on the data contained within. By separating the "what" (the descriptor) from the "how" (the implementation), OpenClaude achieves automatic extensibility without requiring procedural changes when adding new providers.

## Anatomy of a Model Descriptor

A descriptor is a plain JavaScript object that declares the complete contract for a model or provider. According to the source code in [`src/integrations/descriptors.js`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.js), each descriptor contains:

- **`id`** – A stable identifier used by the CLI (e.g., `claude-sonnet-3.5`, `openai-gpt-4`).
- **`label`** – The human-readable name displayed in UI components and logs.
- **`runtimeMetadataScope`** – The descriptor’s lifecycle context (`catalog` for built-in models, `gateway` for external providers).
- **`capabilities`** – Boolean feature flags such as `supportsVision` and `supportsThinking`.
- **`validation`** and **`setup`** – Authentication requirements, default base URLs, environment variable names, and credential schemas.
- **`transportConfig`** – Protocol specifications (e.g., `openai-compatible`, `local`).

This structure ensures that every part of the CLI—from UI rendering to API routing—draws from identical metadata, eliminating duplicate hard-coded values.

## How Runtime Logic Consumes Descriptors

All runtime operations in OpenClaude follow a consistent pattern: **lookup the descriptor, then behave according to its data**. This pattern is implemented across several key utility modules.

### Descriptor Registry and Lookup

The central registry at [`src/integrations/descriptors.js`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.js) exports helper functions that resolve descriptors at runtime:

- **`getModel(routeId)`** – Retrieves a descriptor by its route identifier.
- **`findModelDescriptorForApiName(apiName)`** – Locates the descriptor matching a specific API name (e.g., `claude-sonnet-4-6`), as implemented in [`src/utils/visionUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/visionUtils.ts) at lines 81–84.
- **`findModelDescriptorForApiNameWithRoute(apiName, routeId)`** – Variant that includes route-specific resolution logic.

When a user requests a model like `claude-sonnet-4-6`, the CLI resolves the descriptor first, then examines its `capabilities` object to determine available features.

### Capability-Based Feature Gates

Instead of maintaining hard-coded lists of which models support vision or thinking, OpenClaude checks the descriptor’s `capabilities` field. In [`src/utils/visionUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/visionUtils.ts) (lines 181–183), the code validates vision support by reading `descriptor.capabilities.supportsVision`. Similarly, [`src/utils/thinking.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/thinking.ts) (lines 145–147) checks `descriptor.capabilities.supportsThinking` before enabling reasoning features. This declarative approach means new models gain feature support automatically by simply declaring the appropriate flags in their descriptors.

### Provider Validation and Authentication

The [`src/utils/providerValidation.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts) module uses descriptor metadata to enforce authentication requirements. Lines 13–17 demonstrate how the CLI reads the `validation` and `setup` sections to verify environment variables and construct base URLs. When credentials are missing, the error message is pulled directly from the descriptor’s `validation.missingCredentialMessage` field (lines 618–639), ensuring consistent user guidance across all providers.

## Implementation Examples

### Resolving a Model Descriptor

To fetch a descriptor and inspect its capabilities, use the lookup utilities from [`src/utils/visionUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/visionUtils.ts):

```typescript
import { findModelDescriptorForApiName } from '../utils/visionUtils';

// Resolve the descriptor for the Claude Sonnet model
const descriptor = findModelDescriptorForApiName('claude-sonnet-4-6');

if (descriptor) {
  console.log('Model ID:', descriptor.id);
  console.log('Supports Vision?', descriptor.capabilities?.supportsVision);
}

```

This pattern ensures that any logic depending on model metadata remains decoupled from specific model IDs.

### Checking Capabilities Before API Calls

Feature gating via descriptors prevents hard-coded provider checks:

```typescript
import { getModel } from '../integrations/descriptors';

function canThink(routeId: string): boolean {
  const descriptor = getModel(routeId);
  // The descriptor declares whether the model supports "thinking"
  return descriptor?.capabilities?.supportsThinking ?? false;
}

```

As shown in [`src/utils/thinking.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/thinking.ts), this check reads the `supportsThinking` flag directly from the descriptor’s capabilities object.

### Validating Credentials

Authentication validation is driven entirely by descriptor metadata:

```typescript
import { getDescriptorValidationError } from '../utils/providerValidation';

async function ensureAuth(routeId: string) {
  const error = await getDescriptorValidationError(routeId);
  if (error) {
    throw new Error(error); // Message comes from the descriptor's validation data
  }
}

```

The `getDescriptorValidationError` function inspects the descriptor’s `validation` section to verify that required environment variables are present before allowing API calls to proceed.

## Summary

- **Descriptor-first design** centralizes all model metadata—IDs, labels, capabilities, and validation rules—into reusable objects defined in [`src/integrations/descriptors.js`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.js).
- **Runtime adaptation** occurs because the CLI consults descriptors before executing logic, enabling automatic support for new providers without code changes.
- **Capability flags** (`supportsVision`, `supportsThinking`) in [`src/utils/visionUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/visionUtils.ts) and [`src/utils/thinking.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/thinking.ts) eliminate hard-coded feature lists.
- **Validation logic** in [`src/utils/providerValidation.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts) uses descriptor metadata to enforce authentication requirements and generate contextual error messages.

## Frequently Asked Questions

### What makes the descriptor-first architecture different from traditional configuration?

Traditional CLIs often embed provider logic in conditional statements scattered throughout the codebase. OpenClaude’s descriptor-first architecture inverts this relationship: the descriptor is the primary artifact, and all runtime logic—from routing to UI rendering—consults this metadata object first. This ensures that adding a new provider requires only defining a new descriptor entry, with zero changes to procedural code.

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

You register a new provider by adding a descriptor entry to the registry in [`src/integrations/descriptors.js`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.js). Your descriptor must specify the `id`, `runtimeMetadataScope` (typically `gateway` for external providers), `capabilities`, `validation` rules, and `transportConfig`. Once registered, functions like `getModel` and `getDescriptorValidationError` will automatically recognize and validate the new provider.

### Where does OpenClaude store the descriptor definitions?

The generated array of all descriptors lives in [`src/integrations/descriptors.js`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.js). This file acts as the central registry, consumed by utility modules including [`src/utils/visionUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/visionUtils.ts), [`src/utils/thinking.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/thinking.ts), and [`src/utils/providerValidation.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts) to perform model lookups and capability checks.

### How does the CLI handle capability checks for features like vision or thinking?

Rather than checking against hard-coded model names, the CLI inspects the descriptor’s `capabilities` object. For example, [`src/utils/visionUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/visionUtils.ts) checks `descriptor.capabilities.supportsVision` (lines 181–183), and [`src/utils/thinking.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/thinking.ts) checks `descriptor.capabilities.supportsThinking` (lines 145–147). If the flag is present and true, the feature is enabled; if the descriptor lacks the flag or sets it to false, the feature is disabled.