Skip to main content
Glama
airflow-unfactor

airflow-unfactor

by gabcoyne
README.md
# airflow-unfactor

[![Tests](https://github.com/gabcoyne/airflow-unfactor/actions/workflows/test.yml/badge.svg)](https://github.com/gabcoyne/airflow-unfactor/actions/workflows/test.yml)
[![PyPI](https://img.shields.io/pypi/v/airflow-unfactor)](https://pypi.org/project/airflow-unfactor/)
[![License](https://img.shields.io/github/license/gabcoyne/airflow-unfactor)](LICENSE)

An MCP server that converts Apache Airflow DAGs into Prefect flows. Point it at a DAG, and the LLM generates idiomatic Prefect code. Not a template with TODOs — working code. Built with [FastMCP](https://github.com/jlowin/fastmcp).

## Install

[![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=airflow-unfactor&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJhaXJmbG93LXVuZmFjdG9yIl19)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=airflow-unfactor&config=%7B%22name%22%3A%22airflow-unfactor%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22airflow-unfactor%22%5D%7D)

**Claude Code** — one line:

```bash
claude mcp add airflow-unfactor -- uvx airflow-unfactor
```

**Claude Desktop** and other clients — see [manual config](#manual-config) below.

Then ask your LLM: *"Convert the DAG in `dags/my_etl.py` to a Prefect flow."*

## How It Works

The server exposes seven tools over MCP. The LLM reads raw DAG source code, looks up translation knowledge, and generates the Prefect flow.

| Tool | What It Does |
|------|-------------|
| `read_dag` | Returns raw DAG source code with metadata (path, size, line count) |
| `lookup_concept` | Airflow→Prefect translation knowledge — operators, patterns, connections |
| `validate` | Syntax-checks generated code and returns both sources for comparison |
| `search_prefect_docs` | Searches live Prefect docs for anything not in the pre-compiled knowledge |
| `scaffold` | Creates a Prefect project directory structure (not code) |
| `generate_deployment` | Writes prefect.yaml deployment configuration from DAG metadata |
| `generate_migration_report` | Writes MIGRATION.md with conversion decisions and a before-production checklist |

No AST parsing. No template engine. The LLM reads the code directly, just like a developer would.

## Manual config

The buttons above and the `claude mcp add` command both register the server with `uvx`, which downloads it on first run — no separate `pip install` needed. To install the package directly anyway: `pip install airflow-unfactor` or `uv pip install airflow-unfactor`.

<details>
<summary><strong>Claude Desktop</strong> — <code>~/Library/Application Support/Claude/claude_desktop_config.json</code></summary>

```json
{
  "mcpServers": {
    "airflow-unfactor": {
      "command": "uvx",
      "args": ["airflow-unfactor"]
    }
  }
}
```
</details>

<details>
<summary><strong>Claude Code</strong> — <code>.mcp.json</code> in your project</summary>

```json
{
  "mcpServers": {
    "airflow-unfactor": {
      "command": "uvx",
      "args": ["airflow-unfactor"]
    }
  }
}
```
</details>

<details>
<summary><strong>Cursor</strong> — MCP settings</summary>

```json
{
  "mcpServers": {
    "airflow-unfactor": {
      "command": "uvx",
      "args": ["airflow-unfactor"]
    }
  }
}
```
</details>

## Example

**Airflow DAG:**
```python
from airflow import DAG
from airflow.operators.python import PythonOperator

def extract():
    return {"users": [1, 2, 3]}

def transform(ti):
    data = ti.xcom_pull(task_ids="extract")
    return [u * 2 for u in data["users"]]

with DAG("my_etl", ...) as dag:
    t1 = PythonOperator(task_id="extract", python_callable=extract)
    t2 = PythonOperator(task_id="transform", python_callable=transform)
    t1 >> t2
```

**Generated Prefect flow:**
```python
from prefect import flow, task

@task
def extract():
    return {"users": [1, 2, 3]}

@task
def transform(data):
    return [u * 2 for u in data["users"]]

@flow(name="my_etl")
def my_etl():
    data = extract()
    result = transform(data)
    return result
```

The `>>` dependency chain becomes explicit data passing through return values. XCom is gone. It's just Python.

## Translation Knowledge

The server ships with 78 pre-compiled Airflow→Prefect translation entries covering operators, patterns, connections, and core concepts. These are compiled by Colin from live Airflow source and Prefect documentation.

When the pre-compiled knowledge doesn't cover something, `search_prefect_docs` queries the Prefect documentation MCP server at docs.prefect.io in real time.

## Documentation

Full docs: [gabcoyne.github.io/airflow-unfactor](https://gabcoyne.github.io/airflow-unfactor)

## Development

```bash
git clone https://github.com/gabcoyne/airflow-unfactor.git
cd airflow-unfactor
uv sync

# Run tests
uv run pytest

# Lint
uv run ruff check --fix

# Compile translation knowledge
cd colin && colin run
```

## License

MIT — see [LICENSE](LICENSE).

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

Each tool targets a distinct stage of the migration workflow: reading source, lookup translation knowledge, searching docs, validating, scaffolding project, generating deployment config, and writing report. No two tools overlap in purpose or could be confused.

Naming Consistency3/5

Most tools follow a verb_noun pattern (read_dag, lookup_concept, search_prefect_docs, generate_deployment, generate_migration_report), but 'validate' and 'scaffold' are single verbs without objects, breaking the pattern. The naming style is still readable and all lowercase with underscores.

Tool Count5/5

7 tools is well within the ideal 3-15 range and perfectly scoped for the server's purpose: converting Airflow DAGs to Prefect. Each tool earns its place in the workflow without redundancy or bloat.

Completeness4/5

The tool set covers the major stages of migration: reading the source, understanding concepts, verifying, scaffolding, deployment config, and reporting. The only notable gap is the lack of a tool to generate the actual flow code, but this is intentional (the LLM is expected to write it). Minor gaps like automatic metadata extraction are workable.

Maintenance

ActivityInactive
ResponsivenessNo recent community issues