# How the MCP Server Handles Environment Variable Configuration: Precedence, Auth, and Airflow Connection

> Discover how the MCP server in call518/mcp-airflow-api manages environment variable configuration precedence for server settings, auth, and Airflow connections. Learn the override hierarchy.

- Repository: [JungJungIn/mcp-airflow-api](https://github.com/call518/mcp-airflow-api)
- Tags: how-to-guide
- Published: 2026-02-26

---

**The MCP server in `call518/mcp-airflow-api` reads runtime configuration from environment variables with a strict precedence hierarchy—command-line arguments override environment variables, which override hardcoded defaults—split across server settings, authentication, and Airflow API connection parameters.**

The `mcp-airflow-api` repository implements a flexible configuration system that allows operators to deploy the Multi-Chat-Protocol server in diverse environments, from local STDIO debugging to production HTTP deployments. Understanding how environment variables interact with CLI flags and default values is essential for secure, reliable deployments.

## Configuration Categories and Environment Variables

The server organizes configuration into three distinct concerns, each managed through specific environment variables with sensible defaults.

### Server-Side Settings

Located in [`src/mcp_airflow_api/mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/mcp_main.py) (lines 313-335), these variables control the transport layer and logging:

- **`MCP_LOG_LEVEL`** — Controls verbosity; defaults to `"INFO"`
- **`FASTMCP_TYPE`** — Transport protocol; defaults to `"stdio"` (alternative: `"streamable-http"`)
- **`FASTMCP_HOST`** — Bind address for HTTP mode; defaults to `"127.0.0.1"`
- **`FASTMCP_PORT`** — Listen port for HTTP mode; defaults to `8000`

### Authentication Settings

Also handled in [`mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/mcp_main.py) (lines 338-445), these enable optional static token verification for HTTP transports:

- **`REMOTE_AUTH_ENABLE`** — Boolean flag parsed by `_parse_bool_env()`; defaults to `false`
- **`REMOTE_SECRET_KEY`** — Required when auth is enabled; defaults to empty string `""`

### Airflow API Connection Settings

Defined in [`src/mcp_airflow_api/functions.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/functions.py) (lines 51-63, 94-95), these credentials target the upstream Airflow instance:

- **`AIRFLOW_API_BASE_URL`** — Primary endpoint URL; falls back to legacy `AIRFLOW_API_URL` if empty
- **`AIRFLOW_API_VERSION`** — API version string; defaults to `"v1"`
- **`AIRFLOW_API_USERNAME`** — Basic Auth or JWT username
- **`AIRFLOW_API_PASSWORD`** — Basic Auth password or JWT secret

## Precedence Logic: CLI Overrides Environment

The entry-point `main()` function implements a simple but effective precedence pattern. Each configuration value resolves using:

```python
value = args.option or os.getenv("ENV_VAR", default)

```

This means **command-line arguments take highest priority**, followed by environment variables, then hardcoded defaults.

In [`src/mcp_airflow_api/mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/mcp_main.py) (lines 312-324), the log level demonstrates this hierarchy:

```python
log_level = args.log_level or os.getenv("MCP_LOG_LEVEL", "INFO")

```

If you pass `--log-level DEBUG`, it overrides `MCP_LOG_LEVEL=WARNING`. If neither is provided, the server uses `"INFO"`.

## Server Transport Configuration

The transport layer configuration determines how clients connect to the MCP server. The code in [`mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/mcp_main.py) (lines 329-336) reads:

```python
fastmcp_type = args.type or os.getenv("FASTMCP_TYPE", "stdio")
fastmcp_host = args.host or os.getenv("FASTMCP_HOST", "127.0.0.1")
fastmcp_port = int(args.port or os.getenv("FASTMCP_PORT", "8000"))

```

These values are passed directly to `FastMCP.run()` (lines 376-381) to initialize either STDIO or HTTP transport. When using `streamable-http`, the server binds to the specified host and port; with `stdio`, these network settings are ignored.

## Authentication Enablement and Token Verification

Remote authentication is controlled by a custom truthy parser `_parse_bool_env()` (lines 38-43) that recognizes `"true"`, `"1"`, `"yes"`, and `"on"` case-insensitively.

When **`REMOTE_AUTH_ENABLE`** is truthy and `FASTMCP_TYPE` is `streamable-http`, the server:

1. Validates that `fastmcp` provides `StaticTokenVerifier`
2. Requires a non-empty **`REMOTE_SECRET_KEY`**
3. Builds a static token verifier via `_build_static_token_auth()` (lines 666-671)

If authentication is disabled or the transport is STDIO, the server logs a warning and proceeds without token verification, allowing unrestricted local access.

## Airflow API Connection Resolution

All Airflow interactions flow through `airflow_request()` in [`functions.py`](https://github.com/call518/mcp-airflow-api/blob/main/functions.py). The URL construction is version-aware and supports legacy configuration:

```python
base_url = get_api_base_url()  # AIRFLOW_API_BASE_URL or AIRFLOW_API_URL fallback

version = get_api_version()    # AIRFLOW_API_VERSION, default "v1"

full_url = f"{base_url}/{version}{path}"

```

The base URL extraction (lines 51-59) prioritizes `AIRFLOW_API_BASE_URL`, falling back to the legacy `AIRFLOW_API_URL` if the primary variable is empty. The version defaults to `"v1"` (lines 61-64), but setting `AIRFLOW_API_VERSION=v2` triggers JWT token authentication instead of Basic Auth.

Credentials are extracted from `AIRFLOW_API_USERNAME` and `AIRFLOW_API_PASSWORD` (lines 94-95). For API v2, `get_jwt_token()` automatically obtains a Bearer token; for v1, the credentials are encoded as Basic Auth headers.

## Logging Configuration

The selected log level applies globally via `logging.getLogger().setLevel()` and propagates to the module-specific logger. The server explicitly suppresses noisy `aiohttp` logs at the `WARNING` level (lines 315-319) to prevent log flooding during HTTP transport operations.

## Practical Configuration Examples

### Example 1: Production HTTP Deployment with Environment Variables Only

```bash
export MCP_LOG_LEVEL=DEBUG
export FASTMCP_TYPE=streamable-http
export FASTMCP_HOST=0.0.0.0
export FASTMCP_PORT=8080
export REMOTE_AUTH_ENABLE=true
export REMOTE_SECRET_KEY=super-secret-key
export AIRFLOW_API_BASE_URL=http://airflow.example.com/api
export AIRFLOW_API_VERSION=v2
export AIRFLOW_API_USERNAME=admin
export AIRFLOW_API_PASSWORD=admin-pwd

python -m mcp_airflow_api

```

This configuration starts the server in HTTP mode on all interfaces, enforces static token authentication, and communicates with an Airflow v2 endpoint using JWT tokens.

### Example 2: CLI Override During Development

```bash
MCP_LOG_LEVEL=INFO FASTMCP_PORT=9000 \
    python -m mcp_airflow_api --log-level DEBUG --port 12345

```

Result:
- **Log level**: `DEBUG` (CLI flag wins over environment)
- **Port**: `12345` (CLI flag wins over `FASTMCP_PORT`)
- **Other settings**: Inherited from environment or defaults

### Example 3: Local STDIO Testing Without Authentication

```bash
export REMOTE_AUTH_ENABLE=false
python -m mcp_airflow_api --type stdio

```

The server runs with simple STDIO transport, printing a warning that authentication is disabled, suitable for local debugging and integration testing.

## Summary

- **Precedence is strict**: CLI arguments override environment variables, which override defaults.
- **Transport flexibility**: Use `FASTMCP_TYPE` to switch between `stdio` (default) and `streamable-http`.
- **Authentication conditional**: `REMOTE_AUTH_ENABLE` only affects HTTP transport; requires `REMOTE_SECRET_KEY` when active.
- **Airflow connectivity**: Configure via `AIRFLOW_API_BASE_URL`, version-aware with automatic JWT handling for v2.
- **Source locations**: Configuration parsing lives in [`src/mcp_airflow_api/mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/mcp_main.py) (server/auth) and [`src/mcp_airflow_api/functions.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/functions.py) (Airflow connection).

## Frequently Asked Questions

### What is the exact precedence order for configuration values?

Command-line arguments take absolute precedence. If a CLI flag is omitted, the server checks the corresponding environment variable. If the environment variable is unset or empty, it falls back to the hardcoded default defined in the source code. This pattern is implemented consistently in [`src/mcp_airflow_api/mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/mcp_main.py) using `args.option or os.getenv("VAR", default)`.

### Can I enable authentication for STDIO transport?

No. The authentication logic explicitly checks that `FASTMCP_TYPE` is set to `streamable-http` before enabling `StaticTokenVerifier`. If you set `REMOTE_AUTH_ENABLE=true` while using STDIO, the server logs a warning and continues without token verification, as implemented in [`mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/mcp_main.py) lines 338-445.

### Which Airflow API environment variables are mandatory?

`AIRFLOW_API_BASE_URL` (or the legacy `AIRFLOW_API_URL`) must be supplied—the default is an empty string that will cause connection failures. `AIRFLOW_API_USERNAME` and `AIRFLOW_API_PASSWORD` are technically optional depending on your Airflow configuration, but the code in [`functions.py`](https://github.com/call518/mcp-airflow-api/blob/main/functions.py) (lines 94-95) expects them for both Basic Auth and JWT flows.

### How does the server handle boolean environment variables?

The helper function `_parse_bool_env()` in [`mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/mcp_main.py) (lines 38-43) parses boolean flags case-insensitively, accepting `"true"`, `"1"`, `"yes"`, and `"on"` as truthy values. Any other value (including empty strings) evaluates to `false`, making the default `REMOTE_AUTH_ENABLE=false` behavior safe and predictable.