airflow-unfactor
# airflow-unfactor
[](https://github.com/gabcoyne/airflow-unfactor/actions/workflows/test.yml)
[](https://pypi.org/project/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
[](https://cursor.com/install-mcp?name=airflow-unfactor&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJhaXJmbG93LXVuZmFjdG9yIl19)
[](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
Scored across 7 tools
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.
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.
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.
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.