Skip to main content
Glama
airflow-mcp-server

airflow-mcp-server

by ssasuoirafen

airflow-mcp-server

An MCP server that lets Claude inspect and operate Apache Airflow over its REST API. It exposes safe, curated tools (read + a few guarded writes) rather than mirroring the whole API.

Targets Airflow 2 (stable REST API /api/v1). Airflow 3 is intentionally out of scope.

Runs as a local stdio server: each user runs it on their own machine with their own Airflow credentials, which keeps Airflow RBAC intact.

Status

Working read and write tools against Airflow 2; not yet published.

Read: get_airflow_version, get_airflow_health, list_pools, list_dags, get_dag, list_dag_runs, get_dag_run, list_task_instances, get_task_instance, get_task_logs, list_import_errors.

Write (refused when AIRFLOW_MCP_READ_ONLY=true): trigger_dag_run, set_dag_paused, clear_task_instances (supports dry_run to preview, and reopens a finished DAG run by default so cleared tasks actually get scheduled).

Related MCP server: MCP Server for Apache Airflow

Configuration

All settings come from AIRFLOW_MCP_* environment variables (prefixed to avoid clashing with Airflow's own env). See .env.example.

Variable

Required

Default

Notes

AIRFLOW_MCP_BASE_URL

yes

-

e.g. http://localhost:8080 (no /api suffix)

AIRFLOW_MCP_USERNAME / AIRFLOW_MCP_PASSWORD

one auth method

-

Basic auth

AIRFLOW_MCP_API_TOKEN

one auth method

-

Bearer token; wins over basic auth

AIRFLOW_MCP_READ_ONLY

no

false

true disables every write tool

AIRFLOW_MCP_VERIFY_SSL

no

true

AIRFLOW_MCP_TIMEOUT

no

30

seconds

Use with Claude

Run it straight from GitHub - no clone needed (uvx fetches and runs it):

{
  "mcpServers": {
    "airflow": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/ssasuoirafen/airflow-mcp-server",
        "airflow-mcp"
      ],
      "env": {
        "AIRFLOW_MCP_BASE_URL": "http://localhost:8080",
        "AIRFLOW_MCP_USERNAME": "airflow",
        "AIRFLOW_MCP_PASSWORD": "airflow"
      }
    }
  }
}

Pin a version by appending a ref, e.g. git+https://github.com/ssasuoirafen/airflow-mcp-server@v0.1.0.

For local development, point at a checkout instead:

"command": "uv",
"args": ["run", "--directory", "C:\\path\\to\\airflow-mcp-server", "airflow-mcp"]

The package also installs an airflow-mcp-server executable (same thing) for the longer name.

If published to PyPI later, this simplifies to "command": "uvx", "args": ["airflow-mcp"].

Development

uv sync                # install deps
uv run pytest          # unit tests (mocked, no network)
uv run pytest -m e2e   # opt-in live test; needs a .env pointing at a real Airflow 2
uv run airflow-mcp          # run the server (expects an MCP client on stdio)

Roadmap

  1. Foundation + connectivity (version, health). done

  2. Read tools: DAGs, DAG runs, task instances, logs, import errors, pools. done

  3. Safe writes: trigger DAG, pause/unpause, clear/retry tasks (gated by read-only). done

  4. Packaging metadata and LICENSE. done

  5. Publish to PyPI. pending

License

MIT - see LICENSE.

Available Tools

14 tools
clear_task_instancesA
Destructive

Clear task instances so they re-run (retry).

Clearing resets task state and the scheduler re-runs them, so it is destructive - it can re-execute work. Use dry_run=True first to preview exactly which task instances would be affected without changing anything.

Inputs: dag_id (required), dag_run_id, task_ids, include_downstream, +3 more. Returns: task_instances, total_entries

get_airflow_healthA
Read-only

Return Airflow component health: metadatabase, scheduler, triggerer, dag-processor.

Inputs: no named parameters. Returns: metadatabase, scheduler, triggerer, dag_processor

get_airflow_versionA
Read-only

Return the Airflow version reported by the API.

Handy for confirming connectivity and which Airflow major version is in use.

Inputs: no named parameters. Returns: version, git_version

get_dagB
Read-only

Get a single DAG's details by dag_id.

Inputs: dag_id (required). Returns: dag_id, is_paused, is_active, description, +6 more

get_dag_runA
Read-only

Get a single DAG run by dag_id and dag_run_id.

Inputs: dag_id (required), dag_run_id (required). Returns: dag_run_id, dag_id, state, run_type, +6 more

get_task_instanceB
Read-only

Get a single task instance by dag_id, dag_run_id, and task_id.

Inputs: dag_id (required), dag_run_id (required), task_id (required). Returns: task_id, dag_id, dag_run_id, state, +7 more

get_task_logsA
Read-only

Read the log for one task attempt.

Task logs can be large, so by default only the trailing portion is returned (where the error and traceback usually are).

Inputs: dag_id (required), dag_run_id (required), task_id (required), try_number, +1 more. Returns: result

list_dag_runsB
Read-only

List runs of a DAG, most recent first.

Inputs: dag_id (required), limit, offset, state, +1 more. Returns: dag_runs, total_entries

list_dagsA
Read-only

List DAGs with their pause state, schedule, owners, and tags.

Inputs: limit, offset, only_active, paused, +2 more. Returns: dags, total_entries

list_import_errorsA
Read-only

List DAG import errors (parse failures), with filename and stack trace.

The quickest way to find why a DAG is missing from the list or broken.

Inputs: limit, offset. Returns: import_errors, total_entries

list_poolsA
Read-only

List worker pools with their slot usage (occupied/running/queued/open).

Inputs: limit, offset. Returns: pools, total_entries

list_task_instancesA
Read-only

List task instances in a DAG run, with their states and timings.

Inputs: dag_id (required), dag_run_id (required), limit, offset, +1 more. Returns: task_instances, total_entries

set_dag_pausedA
Idempotent

Pause or unpause a DAG.

Inputs: dag_id (required), is_paused (required). Returns: dag_id, is_paused, is_active, description, +6 more

trigger_dag_runB

Trigger a new run of a DAG.

Inputs: dag_id (required), conf, logical_date, dag_run_id, +1 more. Returns: dag_run_id, dag_id, state, run_type, +6 more

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 14 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose. For example, 'get_dag' retrieves a single DAG, while 'list_dags' lists multiple; 'clear_task_instances' resets tasks, distinct from 'trigger_dag_run'. No overlapping functionality.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun pattern in snake_case (e.g., 'get_dag', 'list_pools', 'set_dag_paused'). Verbs like 'get' for single entities, 'list' for collections, 'clear', 'set', and 'trigger' are used uniformly.

    Tool Count5/5

    14 tools is well-scoped for an Airflow MCP server. Core operations for DAGs, runs, task instances, pools, health, and version are covered without bloat or excessive specialization.

    Completeness4/5

    The toolset covers the main lifecycle for DAGs (list, get, pause, trigger), runs, and task instances (list, get, clear, logs). However, it lacks management operations for pools (only list), DAG run deletion, and advanced task retry options, leaving minor gaps for some workflows.

    Related MCP Connectors

    Related MCP Servers