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

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, 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. 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 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 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 (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:

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 (lines 76-85), this block defines which environment variables contain API keys and provides error messages for missing credentials:

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:

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 (lines 23-33 and 122-136), provide a mapModel function that transforms the provider's API response into ModelCatalogEntry objects:

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 (lines 107-122), define fallback URLs, badges, and API key environment variables:

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:

bun run generate-integrations-artifacts

Or perform a full build:

bun run build

This executes 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 provide templates for verification.

Complete Working Example

Here is a complete skeleton for a new gateway descriptor:

// 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: Contains the GatewayDescriptor interface (lines 95-110) that establishes the contract for all gateway configurations.
  • 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: Build script that aggregates all gateway descriptors into the generated artifacts file.
  • 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 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. 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 (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) 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. 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →