How MCP-Airflow-API Handles Dynamic API Version Switching Between Airflow 2.x and 3.0+

MCP-Airflow-API uses the AIRFLOW_API_VERSION environment variable to dynamically switch between Airflow 2.x (v1 API) and Airflow 3.x (v2 API), automatically adapting URL construction, authentication methods, and endpoint selection at runtime.

The call518/mcp-airflow-api repository provides a Model Context Protocol (MCP) server that abstracts Airflow's REST API differences across major versions. By implementing a runtime detection strategy, the library eliminates the need for separate codebases while supporting both legacy Airflow 2.x installations and modern Airflow 3.0+ deployments.

Runtime Version Detection

Environment Variable Configuration

The version detection logic resides in src/mcp_airflow_api/functions.py. The get_api_version() function reads the AIRFLOW_API_VERSION environment variable and normalizes it to lowercase:


# src/mcp_airflow_api/functions.py

def get_api_version():
    """Get API version from environment."""
    return os.getenv("AIRFLOW_API_VERSION", "v1").lower()

If the environment variable is unset, the system defaults to v1, ensuring backward compatibility with existing Airflow 2.x deployments.

Dynamic URL Construction

All HTTP requests route through construct_api_url(), which injects the detected API version into the request path. This function ensures that endpoints are correctly prefixed regardless of which Airflow version is targeted:


# src/mcp_airflow_api/functions.py

def construct_api_url(path: str) -> str:
    base_url = get_api_base_url()
    version   = get_api_version()
    ...
    return f"{base_url}/{version}{path}"

A request to "/dags" resolves to:

  • http://<host>/api/v1/dags when AIRFLOW_API_VERSION=v1 (Airflow 2.x)
  • http://<host>/api/v2/dags when AIRFLOW_API_VERSION=v2 (Airflow 3.x)

Adaptive Authentication Strategies

The airflow_request() function in src/mcp_airflow_api/functions.py implements version-specific authentication logic to handle the transition from Basic Auth in Airflow 2.x to JWT-based authentication in Airflow 3.x.

Basic Auth for Airflow 2.x (v1)

When get_api_version() returns "v1", the system uses aiohttp.BasicAuth with the configured username and password:

auth = aiohttp.BasicAuth(username, password)

JWT Token Auth for Airflow 3.x (v2)

For Airflow 3.x compatibility, the v2 API path attempts to retrieve a JWT token via get_jwt_token(). The token is cached internally (_jwt_token) for approximately 23 hours to minimize authentication overhead. If JWT retrieval fails, the system gracefully falls back to Basic Auth:


# src/mcp_airflow_api/functions.py (excerpt)

if api_version == "v2":
    try:
        jwt_token = await get_jwt_token()
        headers["Authorization"] = f"Bearer {jwt_token}"
    except Exception:
        auth = aiohttp.BasicAuth(username, password)   # fallback

elif api_version == "v1":
    auth = aiohttp.BasicAuth(username, password)

Tool Registration Architecture

MCP-Airflow-API implements a shared tool pattern that avoids code duplication while supporting both API versions through dependency injection.

Shared Implementation Pattern

The 43 core MCP tools—including list_dags, trigger_dag, and get_health—are implemented once in src/mcp_airflow_api/tools/common_tools.py. These tools reference a module-level airflow_request variable that is injected at registration time.

Version-Specific Registration Modules

The v1_tools.py and v2_tools.py modules handle version-specific registration by binding the appropriate request function:


# src/mcp_airflow_api/tools/v1_tools.py

from ..functions import airflow_request as airflow_request_v1
...
common_tools.airflow_request = airflow_request_v1
common_tools.register_common_tools(mcp)

# src/mcp_airflow_api/tools/v2_tools.py

from ..functions import airflow_request as airflow_request_v2
...
common_tools.airflow_request = airflow_request_v2
common_tools.register_common_tools(mcp)

This architecture ensures that the same business logic executes against the correct API version without maintaining separate tool implementations.

Version-Aware Endpoint Selection

Even within shared tools, specific endpoints vary between Airflow versions. The get_health tool in common_tools.py demonstrates runtime endpoint selection:


# src/mcp_airflow_api/tools/common_tools.py

from ..functions import get_api_version
...
if get_api_version() == "v2":
    resp = await airflow_request("GET", "/monitor/health")   # Airflow 3.x

else:
    resp = await airflow_request("GET", "/health")          # Airflow 2.x

Similarly, Airflow 2.x-specific features such as user/role management and the dataset API include version guards that return explicit "not available" messages when v2 is active, preventing errors against non-existent endpoints.

Runtime Configuration Examples

Environment Setup

Configure the target Airflow version using environment variables before initializing the MCP server:


# Use Airflow 2.x (v1 API)

export AIRFLOW_API_VERSION=v1
export AIRFLOW_API_BASE_URL=http://localhost:8080/api
export AIRFLOW_API_USERNAME=admin
export AIRFLOW_API_PASSWORD=admin

# Use Airflow 3.x (v2 API)

export AIRFLOW_API_VERSION=v2

Python Implementation

The same Python code works across both versions:

from mcp_airflow_api.mcp_main import MCP

mcp = MCP()
await mcp.register_tools()           # registers the correct set based on AIRFLOW_API_VERSION

dags = await mcp.tools.list_dags(limit=5)   # Calls the shared implementation

print(dags)

Direct Version-Specific Invocation

For scenarios requiring explicit version control, use the wrapper functions:

from mcp_airflow_api.functions import airflow_request_v2

# Force a v2 call regardless of the global setting

resp = await airflow_request_v2("GET", "/dags")
print(resp.json())

Summary

MCP-Airflow-API achieves seamless dynamic API version switching through a layered architecture:

  • Environment-driven detection via AIRFLOW_API_VERSION with a safe default to v1 for backward compatibility
  • Dynamic URL construction that injects the correct version prefix into all API endpoints
  • Adaptive authentication that selects Basic Auth for Airflow 2.x and JWT tokens (with Basic fallback) for Airflow 3.x
  • Shared tool implementation with dependency injection, avoiding code duplication while supporting both versions through v1_tools.py and v2_tools.py
  • Runtime endpoint selection within tools to handle version-specific paths like /health vs /monitor/health

This design allows operators to upgrade from Airflow 2.x to 3.x without modifying client code, changing only the AIRFLOW_API_VERSION environment variable.

Frequently Asked Questions

How do I configure MCP-Airflow-API for Airflow 3.x?

Set the AIRFLOW_API_VERSION environment variable to v2 before starting your application. The library will automatically use JWT authentication and v2 API endpoints. If you are running Airflow 2.x, either leave this unset or explicitly set it to v1 to use Basic Auth against the v1 API.

What authentication method does MCP-Airflow-API use for Airflow 2.x?

For Airflow 2.x (v1 API), MCP-Airflow-API uses Basic Authentication with username and password credentials configured via environment variables. This is implemented in src/mcp_airflow_api/functions.py using aiohttp.BasicAuth.

Can I use both API versions simultaneously in the same application?

Yes, through the version-specific wrapper functions airflow_request_v1() and airflow_request_v2() defined in src/mcp_airflow_api/functions.py. These functions temporarily override the global AIRFLOW_API_VERSION environment variable for a single request, allowing you to query both Airflow 2.x and 3.x instances from the same process.

Where are the version-specific tool registrations defined?

Version-specific registrations are located in src/mcp_airflow_api/tools/v1_tools.py for Airflow 2.x and src/mcp_airflow_api/tools/v2_tools.py for Airflow 3.x. Both modules import the shared tool implementations from common_tools.py and inject the appropriate airflow_request function before registering tools with the MCP server.

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 →