# How to Define Custom Provider Descriptors for OpenClaude

> Define custom provider descriptors for OpenClaude by exporting environment variables like PROVIDER_BASE_URL and PROVIDER_API_KEY. Integrate your unique services seamlessly.

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

---

**To define a custom provider descriptor for OpenClaude, export environment variables prefixed with your provider ID: `PROVIDER_BASE_URL`, `PROVIDER_API_KEY`, plus optional `PROVIDER_MODEL_MAP`, `PROVIDER_CONTEXT_WINDOW`, and `PROVIDER_MAX_OUTPUT_TOKENS`.**

OpenClaude discovers and interacts with AI providers through **provider descriptors**. A descriptor tells the CLI how to reach a provider, which environment variables supply its credentials, and how to map model identifiers to the provider's API. When you need a provider that is not shipped out-of-the-box, you create a custom provider descriptor that the runtime consumes using the same code paths as built-in providers.

## Required Descriptor Fields

Every custom provider descriptor must specify three core fields. OpenClaude reads these from environment variables and merges them into the internal provider registry at startup.

| Field | Environment Variable Pattern | Purpose |
|-------|------------------------------|---------|
| `id` | N/A (you choose this) | Short unique name used with `--provider <id>` |
| `baseUrl` | `{PREFIX}_BASE_URL` | Full HTTP URL of the provider's `/v1` endpoint |
| `apiKeyEnv` | `{PREFIX}_API_KEY` | Name of the variable holding the secret key |

The `PREFIX` is your provider ID uppercased with hyphens replaced by underscores. For a provider named `my-custom-provider`, the prefix becomes `MY_CUSTOM_PROVIDER`.

## Optional Descriptor Fields

You can extend the descriptor with additional configuration for models that don't expose metadata through standard endpoints.

| Field | Environment Variable Pattern | Purpose |
|-------|------------------------------|---------|
| `modelMap` | `{PREFIX}_MODEL_MAP` | JSON object mapping OpenAI-style names to provider-native identifiers |
| `contextWindow` | `{PREFIX}_CONTEXT_WINDOW` | Maximum context size when not available in `/v1/models` |
| `maxOutputTokens` | `{PREFIX}_MAX_OUTPUT_TOKENS` | Maximum tokens generated per request |

## Where the Descriptor Logic Lives

The OpenClaude source code implements custom provider discovery in three key locations:

- **Provider discovery** — [`src/utils/model/providers.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/model/providers.ts) normalizes both built-in and custom descriptors.
- **Web-search custom provider** — [`src/tools/WebSearchTool/providers/custom.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/tools/WebSearchTool/providers/custom.ts) demonstrates a minimal provider using only a URL template.
- **CLI flag handling** — [`src/utils/envFile.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/envFile.ts) loads additional environment files via `--provider-env-file`.

These implementations are documented at [`docs/integrations/overview.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/integrations/overview.md) and [`docs/integrations/how-to/add-model.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/integrations/how-to/add-model.md).

## Defining a Custom Provider: Complete Example

### Step 1: Configure Environment Variables

Create a `.env` file or export variables directly in your shell:

```bash
export MY_CUSTOM_PROVIDER_BASE_URL="https://api.my-model.com/v1"
export MY_CUSTOM_PROVIDER_API_KEY="sk-xxxxxxxxxxxxxxxxxxxx"
export MY_CUSTOM_PROVIDER_MODEL_MAP='{"gpt-4":"my-model-v1","claude-3":"my-model-v2"}'
export MY_CUSTOM_PROVIDER_CONTEXT_WINDOW="1000000"
export MY_CUSTOM_PROVIDER_MAX_OUTPUT_TOKENS="32768"

```

### Step 2: Understand the Runtime Discovery

The `getCustomProvider` function in [`src/utils/model/providers.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/model/providers.ts) resolves your descriptor at runtime:

```typescript
// src/utils/model/providers.ts (excerpt)
export interface ProviderDescriptor {
  id: string;                     // e.g. "my-custom-provider"
  baseUrl: string;                // e.g. "https://api.my-model.com/v1"
  apiKeyEnv: string;              // e.g. "MY_CUSTOM_API_KEY"
  modelMap?: Record<string, string>;
  contextWindow?: number;
  maxOutputTokens?: number;
}

export function getCustomProvider(id: string): ProviderDescriptor | undefined {
  const prefix = id.toUpperCase().replace(/-/g, '_');
  const baseUrl = process.env[`${prefix}_BASE_URL`];
  const apiKeyEnv = `${prefix}_API_KEY`;
  if (!baseUrl || !process.env[apiKeyEnv]) return undefined;

  const descriptor: ProviderDescriptor = {
    id,
    baseUrl,
    apiKeyEnv,
    modelMap: tryParseJson(process.env[`${prefix}_MODEL_MAP`]),
    contextWindow: tryParseNumber(process.env[`${prefix}_CONTEXT_WINDOW`]),
    maxOutputTokens: tryParseNumber(process.env[`${prefix}_MAX_OUTPUT_TOKENS`]),
  };
  return descriptor;
}

```

### Step 3: Invoke from the CLI

Reference your custom provider using the `--provider` flag:

```bash

# Load additional env vars from a file (optional)

openclaude --provider-env-file .mycustom.env \
           --provider my-custom-provider \
           "Write a short poem about sunrise"

```

## Alternative: URL-Template Providers for Web Search

For specialized use cases like custom web search, OpenClaude supports an even leaner pattern. The [`src/tools/WebSearchTool/providers/custom.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/tools/WebSearchTool/providers/custom.ts) file implements a provider that requires only a URL template:

```typescript
// src/tools/WebSearchTool/providers/custom.ts (excerpt)
export const customProvider = {
  id: 'custom',
  isConfigured: () => Boolean(process.env.WEB_URL_TEMPLATE),
  urlTemplate: process.env.WEB_URL_TEMPLATE,   // e.g. "https://api.my-search.com/v2?q={query}"
  // Re-uses generic WebSearchTool logic — no additional code required
};

```

This pattern demonstrates how OpenClaude's provider system scales from full API descriptors down to simple configuration-driven integrations.

## Global Overrides for Model Metadata

When your custom provider does not publish model metadata through a `/v1/models` endpoint, you can supply context window and token limits through global environment variables:

```bash
export CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS='{"my-model":"1000000"}'
export CLAUDE_CODE_OPENAI_MAX_OUTPUT_TOKENS='{"my-model":"32768"}'

```

These overrides are documented in [`docs/advanced-setup.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/advanced-setup.md) under the sections for `CLADE_CODE_OPENAI_CONTEXT_WINDOWS` and `CLAUDE_CODE_OPENAI_MAX_OUTPUT_TOKENS`. They apply to all providers when per-provider configuration is unavailable.

## Key Files Reference

| File | Role |
|------|------|
| [`src/utils/model/providers.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/model/providers.ts) | Core aggregation logic for built-in and custom provider descriptors |
| [`src/tools/WebSearchTool/providers/custom.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/tools/WebSearchTool/providers/custom.ts) | Minimal provider example using URL templates |
| [`src/utils/envFile.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/envFile.ts) | Extra `.env` file loading via `--provider-env-file` |
| [`docs/integrations/overview.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/integrations/overview.md) | High-level provider system documentation |
| [`docs/integrations/how-to/add-model.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/integrations/how-to/add-model.md) | Step-by-step model addition guide |
| [`docs/advanced-setup.md`](https://github.com/Gitlawb/openclaude/blob/main/docs/advanced-setup.md) | Context-window and max-output-token override details |

## Summary

- **Custom provider descriptors** enable OpenClaude integration with any OpenAI-compatible API endpoint without code changes.
- **Required fields**: `baseUrl` and `apiKeyEnv` (derived from `{PREFIX}_BASE_URL` and `{PREFIX}_API_KEY` environment variables).
- **Optional fields**: `modelMap`, `contextWindow`, and `maxOutputTokens` for providers lacking standard metadata endpoints.
- **Source implementation**: [`src/utils/model/providers.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/model/providers.ts) handles runtime discovery and normalization.
- **CLI usage**: Pass `--provider <id>` and optionally `--provider-env-file <path>` to load configuration.

## Frequently Asked Questions

### How do I name my custom provider ID?

Choose a lowercase string with hyphens separating words, such as `my-org-llm`. OpenClaude converts this to `MY_ORG_LLM` when constructing environment variable names. Avoid spaces, special characters, or uppercase letters in the ID itself.

### Can I use multiple custom providers simultaneously?

OpenClaude supports one active provider per invocation via `--provider`. Define multiple providers in your environment by using distinct IDs with unique prefixes, then switch between them by changing the CLI flag value.

### What happens if my provider doesn't have an API key?

The `getCustomProvider` function in [`src/utils/model/providers.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/model/providers.ts) returns `undefined` when `{PREFIX}_API_KEY` is unset, and OpenClaude falls back to built-in providers or exits with a configuration error. Self-hosted endpoints without authentication still require the variable to exist, potentially with an empty or dummy value.

### Where should I store sensitive provider credentials?

Use a dedicated `.env` file loaded via `--provider-env-file` rather than exporting variables in your shell history. The [`src/utils/envFile.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/envFile.ts) module parses these files and injects values into `process.env` before provider discovery runs, keeping secrets out of command history and process listings.