# How to Author a New Gateway Descriptor for OpenClaude: A Complete Implementation Guide

> Learn to author a new gateway descriptor for OpenClaude with this complete implementation guide. Create a TypeScript file, define configuration with defineGateway(), and register it.

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

---

**You author a new gateway descriptor by creating a TypeScript file in `src/integrations/gateways/`, defining the configuration using the `defineGateway()` helper, and running the artifact generator to register it with the runtime.**

OpenClaude discovers and routes requests to external AI providers through strongly-typed gateway descriptors. According to the Gitlawb/openclaude source code, these descriptors are TypeScript objects that specify base URLs, authentication methods, and model catalogs for OpenAI-compatible hosts. The system relies on compile-time code generation to automatically bundle provider configurations without manual registry updates.

## Understanding the Gateway Descriptor Architecture

A gateway descriptor is a typed object that tells the OpenClaude runtime how to communicate with a specific AI service. The core interface definition lives in [`src/integrations/descriptors.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.ts), where the `GatewayDescriptor` type (lines 95-110) establishes the contract for all provider configurations, including fields for transport, validation, and model discovery.

To make authoring ergonomic, the codebase provides a helper function in [`src/integrations/define.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/define.ts). The `defineGateway()` function (lines 14-20) returns the configuration object unchanged while providing full TypeScript intellisense and compile-time validation against the `GatewayDescriptor` interface.

The runtime registry in [`src/integrations/registry.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/registry.ts) exposes getters like `getGateway()` (lines 73-78), but it does not manually import each gateway. Instead, the artifact generator in [`scripts/generate-integrations-artifacts.ts`](https://github.com/Gitlawb/openclaude/blob/main/scripts/generate-integrations-artifacts.ts) performs a glob search of `src/integrations/gateways/*.ts`, collects all default exports, and bundles them into the `GATEWAY_DESCRIPTORS` constant in [`src/integrations/generated/integrationArtifacts.generated.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/generated/integrationArtifacts.generated.ts) (lines 95-100).

## Step-by-Step Guide to Authoring a Gateway Descriptor

### 1. Create the Gateway File

Create a new file at `src/integrations/gateways/<your-gateway>.ts`. The artifact generator specifically scans this directory during the build process, so placing your file here is essential for automatic discovery. While the filename organizes your source code, the `id` field inside the descriptor determines the runtime identifier.

### 2. Import Required Types and Helpers

Import the `defineGateway` function and any utility types needed for model mapping:

```typescript
import { defineGateway } from '../define.js';
import type { ModelCatalogEntry } from '../descriptors.js';

```

Using `defineGateway` ensures your object satisfies the `GatewayDescriptor` interface, catching shape errors at compile time.

### 3. Define the Core Configuration

Export a default descriptor using `defineGateway()`, filling in required fields that drive validation and request formation:

- **`id`**: Unique string identifier for runtime lookups
- **`label`**: Human-readable name displayed in the UI
- **`category`**: Classification such as `'aggregating'`, `'hosted'`, or `'local'`
- **`defaultBaseUrl`**: The endpoint URL for API requests
- **`defaultModel`**: Fallback model identifier when none is specified
- **`setup`**: Authentication requirements configuration

### 4. Configure Validation Rules

Add a `validation` block to specify credential requirements. As implemented in the reference gateway at [`src/integrations/gateways/gitlawb-opengateway.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/gateways/gitlawb-opengateway.ts) (lines 76-85), this block defines which environment variables contain API keys and provides error messages for missing credentials:

```typescript
validation: {
  kind: 'credential-env',
  credentialEnvVars: ['MYGATEWAY_API_KEY'],
  missingCredentialMessage: 'MYGATEWAY_API_KEY is required – obtain one from https://mygateway.com/keys',
  routing: { matchBaseUrlHosts: ['api.mygateway.com'] },
}

```

The runtime refuses to instantiate the gateway without valid credentials present in the environment.

### 5. Set Up Transport Configuration

Most gateways use the `openai-compatible` transport kind with an OpenAI shim. Configure the `transportConfig` object to specify headers, authentication schemes, and parameter mappings:

```typescript
transportConfig: {
  kind: 'openai-compatible',
  openaiShim: {
    headers: { 'Accept-Encoding': 'identity' },
    defaultAuthHeader: { name: 'authorization', scheme: 'bearer' },
    maxTokensField: 'max_tokens',
  },
}

```

This configuration ensures requests are formatted correctly for the target host's expectations.

### 6. Implement Model Catalog Discovery

Define a `catalog` block to enable dynamic model discovery. As shown in [`src/integrations/gateways/gitlawb-opengateway.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/gateways/gitlawb-opengateway.ts) (lines 23-33 and 122-136), provide a `mapModel` function that transforms the provider's API response into `ModelCatalogEntry` objects:

```typescript
catalog: {
  source: 'hybrid',
  discovery: {
    kind: 'openai-compatible',
    requiresAuth: false,
    mapModel: mapMyGatewayModel,
  },
  discoveryCacheTtl: '1d',
  discoveryRefreshMode: 'startup',
  allowManualRefresh: true,
  models: [
    {
      id: 'mygateway-auto',
      apiName: 'auto',
      label: 'Auto – Smart Routing',
    },
  ],
},

```

Setting `source: 'hybrid'` combines static model definitions with dynamic discovery results.

### 7. Add UI Presets (Optional)

The `preset` field provides ready-made configuration profiles for the interface. Following the example in [`src/integrations/gateways/gitlawb-opengateway.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/gateways/gitlawb-opengateway.ts) (lines 107-122), define fallback URLs, badges, and API key environment variables:

```typescript
preset: {
  id: 'mygateway',
  description: 'My Gateway – API key required.',
  apiKeyEnvVars: ['MYGATEWAY_API_KEY'],
  label: 'My Gateway',
  badge: { text: 'Recommended', color: 'success' },
  fallbackBaseUrl: 'https://api.mygateway.com/v1',
}

```

### 8. Generate Integration Artifacts

Run the artifact generator to register your gateway:

```bash
bun run generate-integrations-artifacts

```

Or perform a full build:

```bash
bun run build

```

This executes [`scripts/generate-integrations-artifacts.ts`](https://github.com/Gitlawb/openclaude/blob/main/scripts/generate-integrations-artifacts.ts), which imports your new file and adds its default export to the `GATEWAY_DESCRIPTORS` array without requiring manual registry updates.

### 9. Test Your Implementation

Create a test file at `src/integrations/gateways/<your-gateway>.test.ts` that imports the descriptor via `getGateway('your-id')` from the registry. Assert key properties including validation rules, transport configuration, and catalog shape. Existing tests such as [`gitlawb-opengateway.test.ts`](https://github.com/Gitlawb/openclaude/blob/main/gitlawb-opengateway.test.ts) provide templates for verification.

## Complete Working Example

Here is a complete skeleton for a new gateway descriptor:

```typescript
// src/integrations/gateways/mygateway.ts
import { defineGateway } from '../define.js';
import type { ModelCatalogEntry } from '../descriptors.js';

function mapMyGatewayModel(raw: unknown): ModelCatalogEntry | null {
  // Transform remote payload into ModelCatalogEntry
  if (typeof raw === 'object' && raw !== null && 'id' in raw) {
    return {
      id: String((raw as any).id),
      apiName: String((raw as any).id),
      label: String((raw as any).name || (raw as any).id),
    };
  }
  return null;
}

export default defineGateway({
  id: 'mygateway',
  label: 'My Awesome Gateway',
  category: 'aggregating',
  defaultBaseUrl: 'https://api.mygateway.com/v1',
  defaultModel: 'my-default-model',
  supportsModelRouting: true,
  vendorId: 'openai',
  
  setup: {
    requiresAuth: true,
    authMode: 'api-key',
    credentialEnvVars: ['MYGATEWAY_API_KEY', 'OPENAI_API_KEY'],
  },
  
  validation: {
    kind: 'credential-env',
    credentialEnvVars: ['MYGATEWAY_API_KEY', 'OPENAI_API_KEY'],
    missingCredentialMessage:
      'MYGATEWAY_API_KEY is required – obtain one from https://mygateway.com/keys',
    routing: { matchBaseUrlHosts: ['api.mygateway.com'] },
  },
  
  transportConfig: {
    kind: 'openai-compatible',
    openaiShim: {
      headers: { 'Accept-Encoding': 'identity' },
      defaultAuthHeader: { name: 'authorization', scheme: 'bearer' },
      maxTokensField: 'max_tokens',
    },
  },
  
  preset: {
    id: 'mygateway',
    description: 'My Gateway – API key required.',
    apiKeyEnvVars: ['MYGATEWAY_API_KEY'],
    label: 'My Gateway',
    name: 'My Gateway',
    badge: { text: 'Recommended', color: 'success' },
    vendorId: 'openai',
    baseUrlEnvVars: ['MYGATEWAY_BASE_URL', 'OPENAI_BASE_URL'],
    fallbackBaseUrl: 'https://api.mygateway.com/v1',
    fallbackModel: 'my-default-model',
  },
  
  catalog: {
    source: 'hybrid',
    discovery: {
      kind: 'openai-compatible',
      requiresAuth: false,
      mapModel: mapMyGatewayModel,
    },
    discoveryCacheTtl: '1d',
    discoveryRefreshMode: 'startup',
    allowManualRefresh: true,
    models: [
      {
        id: 'mygateway-auto',
        apiName: 'auto',
        label: 'Auto – Smart Routing (via MyGateway)',
        notes: 'Gateway picks the cheapest capable model.',
      },
    ],
  },
});

```

## Key Files in the Gateway System

- **[`src/integrations/descriptors.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/descriptors.ts)**: Contains the `GatewayDescriptor` interface (lines 95-110) that establishes the contract for all gateway configurations.
- **[`src/integrations/define.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/define.ts)**: Exports the `defineGateway()` helper function (lines 14-20) that provides type checking while returning the descriptor object unchanged.
- **`src/integrations/gateways/`**: Directory where individual gateway descriptor files are stored and automatically discovered by the build system.
- **[`scripts/generate-integrations-artifacts.ts`](https://github.com/Gitlawb/openclaude/blob/main/scripts/generate-integrations-artifacts.ts)**: Build script that aggregates all gateway descriptors into the generated artifacts file.
- **[`src/integrations/registry.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/registry.ts)**: Runtime registry (lines 73-78) providing `getGateway()` and other accessors for retrieving descriptor configurations.

## Summary

- **Gateway descriptors** are TypeScript objects in `src/integrations/gateways/` that define how OpenClaude communicates with external AI providers.
- Use **`defineGateway()`** from [`src/integrations/define.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/define.ts) to ensure type safety when authoring your configuration.
- Include **validation rules** to enforce credential requirements before the runtime attempts to use the gateway.
- Configure **transport settings** using the `openai-compatible` kind for standard OpenAI API emulation.
- Implement **model catalog discovery** with a `mapModel` function to translate provider-specific payloads into standard entries.
- Run **`bun run generate-integrations-artifacts`** to compile your descriptor into the runtime registry without manual registration steps.

## Frequently Asked Questions

### What is the minimum required configuration for a gateway descriptor?

The minimum configuration requires the `id`, `label`, `category`, `defaultBaseUrl`, `defaultModel`, `setup`, and `transportConfig` fields. While the `catalog` and `preset` fields are optional, adding a `validation` block is strongly recommended to ensure proper error messaging when credentials are missing. The runtime will not instantiate a gateway that requires authentication without proper validation rules defined.

### How does OpenClaude discover new gateway files automatically?

The discovery mechanism relies on the artifact generator in [`scripts/generate-integrations-artifacts.ts`](https://github.com/Gitlawb/openclaude/blob/main/scripts/generate-integrations-artifacts.ts). At build time, this script performs a glob search for all `.ts` files in `src/integrations/gateways/`, imports each module, and extracts the default export to build the `GATEWAY_DESCRIPTORS` constant in [`src/integrations/generated/integrationArtifacts.generated.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/generated/integrationArtifacts.generated.ts) (lines 95-100). This compile-time approach eliminates the need for manual registry imports or maintenance of an index file.

### Can I reference environment variables other than API keys in my gateway descriptor?

Yes. While the `credentialEnvVars` array in the `validation` block specifically handles authentication requirements, the `preset` configuration supports `baseUrlEnvVars` for dynamic endpoint configuration. You can also reference arbitrary environment variables in the `transportConfig` headers or within your model mapping functions, though credentials should always be declared in the `validation` section to ensure proper security validation by the runtime before any network requests occur.

### What is the difference between the filename and the `id` field when creating a gateway?

The filename (e.g., [`mygateway.ts`](https://github.com/Gitlawb/openclaude/blob/main/mygateway.ts)) determines where the artifact generator finds your source code, while the `id` field inside the descriptor determines the runtime identifier used with `getGateway()` from [`src/integrations/registry.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/integrations/registry.ts). These values can differ, but keeping them aligned prevents configuration confusion. The `id` must be unique across all gateways and serves as the primary lookup key for the runtime registry.