> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbifrost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Configuration

> Configure the Bifrost client in config.json - connection pool, logging, CORS, header filtering, compat shims, and MCP settings

The `client` block controls how Bifrost manages its internal worker pool, request logging, authentication enforcement, header policies, SDK compatibility shims, and MCP agent behaviour.

***

## Connection Pool

| Field                  | Type    | Default | Description                                                            |
| ---------------------- | ------- | ------- | ---------------------------------------------------------------------- |
| `initial_pool_size`    | integer | `300`   | Pre-allocated worker goroutines per provider queue                     |
| `drop_excess_requests` | boolean | `false` | Drop requests when queue is full instead of waiting (returns HTTP 429) |

A larger pool reduces latency spikes under burst load at the cost of higher baseline memory. `500–1000` is a common starting point for production workloads with multiple providers.

```json theme={null}
{
  "client": {
    "initial_pool_size": 1000,
    "drop_excess_requests": true
  }
}
```

***

## Request & Response Logging

| Field                     | Type             | Default | Description                                           |
| ------------------------- | ---------------- | ------- | ----------------------------------------------------- |
| `enable_logging`          | boolean          | -       | Log all LLM requests and responses                    |
| `disable_content_logging` | boolean          | `false` | Strip message content from logs (keeps metadata only) |
| `log_retention_days`      | integer          | `365`   | Days to retain log entries in the store               |
| `logging_headers`         | array of strings | `[]`    | HTTP request headers to capture in log metadata       |

Set `disable_content_logging: true` for HIPAA / PCI compliance workloads where message content must not be persisted.

```json theme={null}
{
  "client": {
    "enable_logging": true,
    "disable_content_logging": true,
    "log_retention_days": 90,
    "logging_headers": ["x-request-id", "x-user-id"]
  }
}
```

***

## Reverse Proxy

When Bifrost runs behind a reverse proxy, the OAuth URLs it advertises and uses are built from the incoming `Host` header by default - which is the proxy's internal address, not its public one. Two settings let you override this with the proxy's public URL.

| Field                     | Type             | Default | Description                                                                     |
| ------------------------- | ---------------- | ------- | ------------------------------------------------------------------------------- |
| `mcp_external_server_url` | string or EnvVar | -       | Public base URL Bifrost advertises in OAuth server metadata                     |
| `mcp_external_client_url` | string or EnvVar | -       | Public base URL Bifrost uses as the `redirect_uri` against upstream MCP servers |

Both fields support env var syntax (`"env.MY_VAR"`). When unset, the field falls back to the incoming `Host` header.

### The two roles

Bifrost participates in OAuth in two directions, and each URL controls one direction:

* **Server URL** is what *downstream MCP clients* read about Bifrost. It is the `issuer` in `/.well-known/oauth-authorization-server`, the `resource` in `/.well-known/oauth-protected-resource`, and the URL inside the `WWW-Authenticate` header on `/mcp`. Example: Claude Code connects to `https://bifrost.example.com/mcp` and discovers the authorize/token endpoints from this URL.
* **Client URL** is what Bifrost hands to *upstream OAuth providers* as its own callback. It is the `redirect_uri` Bifrost registers with Notion, Jira, GitHub, etc. when it acts as an OAuth client to an MCP server. Upstream providers redirect the user's browser to `<client URL>/api/oauth/callback` after login.

### Common case - one public URL

Most deployments sit behind a single proxy and want both URLs to be the same. Set both to the same value:

```json theme={null}
{
  "client": {
    "mcp_external_server_url": "env.BIFROST_EXTERNAL_URL",
    "mcp_external_client_url": "env.BIFROST_EXTERNAL_URL"
  }
}
```

Or as a plain URL:

```json theme={null}
{
  "client": {
    "mcp_external_server_url": "https://api.yourcompany.com",
    "mcp_external_client_url": "https://api.yourcompany.com"
  }
}
```

### Asymmetric case - different URLs per role

Set them independently when the management/discovery surface and the OAuth callback surface live behind different proxies. For example, when the management UI and `/.well-known` endpoints are exposed on an internal-auth-protected hostname, but upstream OAuth providers need to redirect to a publicly reachable hostname:

```json theme={null}
{
  "client": {
    "mcp_external_server_url": "https://internal.yourcompany.com",
    "mcp_external_client_url": "https://oauth.yourcompany.com"
  }
}
```

You can also set just one and leave the other empty - the unset side falls back to the request's `Host` header.

### When to use which

| Scenario                                                                   | Server URL         | Client URL          |
| -------------------------------------------------------------------------- | ------------------ | ------------------- |
| Single public proxy fronting Bifrost (most common)                         | proxy URL          | same proxy URL      |
| Management/discovery on one hostname, OAuth callbacks on another           | discovery hostname | callback hostname   |
| Bifrost reachable internally but upstream providers need a public callback | leave empty        | public callback URL |
| No reverse proxy (development)                                             | leave empty        | leave empty         |

These settings are also configurable via the UI (**MCP Gateway → MCP Settings**) and the management API, with no restart required.

***

## Security & CORS

| Field                       | Type             | Default | Description                                                                    |
| --------------------------- | ---------------- | ------- | ------------------------------------------------------------------------------ |
| `allowed_origins`           | array            | `["*"]` | CORS allowed origins (use URIs or `"*"`)                                       |
| `enforce_auth_on_inference` | boolean          | `false` | Require auth (virtual key, API key, or user token) on `/v1/*` inference routes |
| `max_request_body_size_mb`  | integer          | `100`   | Maximum allowed request body size in MB                                        |
| `whitelisted_routes`        | array of strings | `[]`    | Routes that bypass auth middleware                                             |
| `allowed_headers`           | array of strings | `[]`    | Additional headers permitted for CORS and WebSocket                            |

```json theme={null}
{
  "client": {
    "allowed_origins": [
      "https://app.yourcompany.com",
      "https://admin.yourcompany.com"
    ],
    "enforce_auth_on_inference": true,
    "max_request_body_size_mb": 50,
    "whitelisted_routes": ["/health", "/metrics"]
  }
}
```

***

## Header Filtering

Controls which `x-bf-eh-*` extra headers are forwarded to upstream LLM providers.

| Field                            | Type             | Default | Description                                                                  |
| -------------------------------- | ---------------- | ------- | ---------------------------------------------------------------------------- |
| `header_filter_config.allowlist` | array of strings | `[]`    | Only these headers are forwarded (whitelist mode)                            |
| `header_filter_config.denylist`  | array of strings | `[]`    | These headers are always blocked                                             |
| `required_headers`               | array of strings | `[]`    | Headers that must be present on every request (rejected with 400 if missing) |

When both `allowlist` and `denylist` are empty, all `x-bf-eh-*` headers pass through. Specifying an `allowlist` enables strict whitelist mode - only listed headers are forwarded.

```json theme={null}
{
  "client": {
    "header_filter_config": {
      "allowlist": [
        "x-bf-eh-anthropic-version",
        "x-bf-eh-openai-beta"
      ],
      "denylist": []
    },
    "required_headers": ["x-request-id"]
  }
}
```

***

## Compat Shims

Compatibility flags that let Bifrost silently adapt request/response shapes for SDK integrations.

| Field                              | Type    | Default | Description                                                  |
| ---------------------------------- | ------- | ------- | ------------------------------------------------------------ |
| `compat.convert_text_to_chat`      | boolean | `false` | Wrap legacy `/v1/completions` text requests as chat messages |
| `compat.convert_chat_to_responses` | boolean | `false` | Translate chat completions to Responses API format           |
| `compat.should_drop_params`        | boolean | `false` | Silently drop unsupported parameters instead of erroring     |
| `compat.should_convert_params`     | boolean | `false` | Auto-convert parameter values across provider schemas        |

```json theme={null}
{
  "client": {
    "compat": {
      "should_drop_params": true,
      "convert_text_to_chat": true
    }
  }
}
```

***

## MCP Agent Settings

| Field                          | Type    | Default | Description                                                         |
| ------------------------------ | ------- | ------- | ------------------------------------------------------------------- |
| `mcp_agent_depth`              | integer | `10`    | Maximum tool-call recursion depth for MCP agent mode                |
| `mcp_tool_execution_timeout`   | integer | `30`    | Timeout per MCP tool execution in seconds                           |
| `mcp_code_mode_binding_level`  | string  | -       | Code mode binding level: `"server"` or `"tool"`                     |
| `mcp_tool_sync_interval`       | integer | `10`    | Global tool sync interval in minutes (`0` = disabled)               |
| `mcp_disable_auto_tool_inject` | boolean | `false` | When `true`, MCP tools are not automatically injected into requests |

```json theme={null}
{
  "client": {
    "mcp_agent_depth": 15,
    "mcp_tool_execution_timeout": 60,
    "mcp_tool_sync_interval": 10
  }
}
```

***

## Async Jobs

| Field                        | Type    | Default | Description                                                  |
| ---------------------------- | ------- | ------- | ------------------------------------------------------------ |
| `async_job_result_ttl`       | integer | `3600`  | TTL (seconds) for async job results                          |
| `disable_db_pings_in_health` | boolean | `false` | Exclude database connectivity from `/health` endpoint checks |

***

## Prometheus Labels

Add custom labels to every Prometheus metric emitted by Bifrost:

```json theme={null}
{
  "client": {
    "prometheus_labels": ["environment=production", "region=us-east-1"]
  }
}
```

***

## Authentication

`governance.auth_config` protects the Bifrost dashboard and management API with username/password auth.

| Field                       | Type    | Default | Description                                 |
| --------------------------- | ------- | ------- | ------------------------------------------- |
| `is_enabled`                | boolean | `false` | Enable username/password auth               |
| `admin_username`            | string  | -       | Admin username                              |
| `admin_password`            | string  | -       | Admin password (use `env.` reference)       |
| `disable_auth_on_inference` | boolean | `false` | Skip auth check on `/v1/*` inference routes |

```json theme={null}
{
  "governance": {
    "auth_config": {
      "is_enabled": true,
      "admin_username": "env.BIFROST_ADMIN_USERNAME",
      "admin_password": "env.BIFROST_ADMIN_PASSWORD",
      "disable_auth_on_inference": false
    }
  }
}
```

<Note>
  A top-level `auth_config` is also accepted for backwards compatibility, but `governance.auth_config` is the preferred location.
</Note>

***

## Encryption Key

```json theme={null}
{
  "encryption_key": "env.BIFROST_ENCRYPTION_KEY"
}
```

| Notes                                                                                           |
| ----------------------------------------------------------------------------------------------- |
| Accepts any string; Bifrost derives a 32-byte AES-256 key using Argon2id                        |
| Can also be set via the `BIFROST_ENCRYPTION_KEY` environment variable                           |
| Once set and the database is populated, the key cannot be changed without clearing the database |
| Omitting the key stores data in plain text - not recommended for production                     |

***

## Full Example

```json theme={null}
{
  "$schema": "https://www.getbifrost.ai/schema",
  "encryption_key": "env.BIFROST_ENCRYPTION_KEY",

  "governance": {
    "auth_config": {
      "is_enabled": true,
      "admin_username": "env.BIFROST_ADMIN_USERNAME",
      "admin_password": "env.BIFROST_ADMIN_PASSWORD",
      "disable_auth_on_inference": false
    }
  },

  "client": {
    "initial_pool_size": 1000,
    "drop_excess_requests": true,

    "enable_logging": true,
    "disable_content_logging": false,
    "log_retention_days": 90,
    "logging_headers": ["x-request-id", "x-user-id"],

    "mcp_external_server_url": "env.BIFROST_EXTERNAL_URL",
    "mcp_external_client_url": "env.BIFROST_EXTERNAL_URL",

    "allowed_origins": ["https://app.yourcompany.com"],
    "enforce_auth_on_inference": true,
    "max_request_body_size_mb": 100,

    "header_filter_config": {
      "allowlist": [],
      "denylist": []
    },
    "required_headers": [],

    "compat": {
      "should_drop_params": false
    },

    "prometheus_labels": ["environment=production"],

    "mcp_agent_depth": 10,
    "mcp_tool_execution_timeout": 30,

    "async_job_result_ttl": 3600
  }
}
```
