Skip to content

Commit c49a69a

Browse files
committed
style: ruff lint and format
1 parent c511a50 commit c49a69a

File tree

8 files changed

+113
-132
lines changed

8 files changed

+113
-132
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ wheels/
2020
.installed.cfg
2121
*.egg
2222
.pypirc
23+
.ruff_cache/
2324

2425
# Virtual Environment
2526
.env

.tool-versions

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
python 3.12.9
1+
python 3.12.6

Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
build:
22
uv run python -m build
3-
43
publish:
54
uv run python -m twine upload --config-file .pypirc dist/*
5+
lint:
6+
uv run ruff check . --fix
7+
format:
8+
uv run ruff format .

pyproject.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-server-apache-airflow"
3-
version = "0.1.0a1"
3+
version = "0.1.3"
44
description = "Model Context Protocol (MCP) server for Apache Airflow"
55
authors = [
66
{ name = "Gyeongmo Yang", email = "[email protected]" }
@@ -20,6 +20,7 @@ classifiers = [
2020
"Programming Language :: Python :: 3",
2121
"Programming Language :: Python :: 3.10",
2222
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
2324
"Topic :: Software Development :: Libraries :: Python Modules",
2425
]
2526
keywords = ["mcp", "airflow", "apache-airflow", "model-context-protocol"]
@@ -51,3 +52,7 @@ include = [
5152
"README.md",
5253
"LICENSE",
5354
]
55+
56+
[tool.ruff]
57+
select = ["E", "W", "F", "B", "I"]
58+
line-length = 120

src/airflow.py

+37-45
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import os
2-
31
import httpx
42
import mcp.types as types
53

6-
AIRFLOW_HOST = os.getenv("AIRFLOW_HOST").rstrip("/")
7-
AIRFLOW_USERNAME = os.getenv("AIRFLOW_USERNAME")
8-
AIRFLOW_PASSWORD = os.getenv("AIRFLOW_PASSWORD")
4+
from src.envs import AIRFLOW_HOST, AIRFLOW_PASSWORD, AIRFLOW_USERNAME
5+
96

107
def get_dag_url(dag_id: str) -> str:
118
return f"{AIRFLOW_HOST}/dags/{dag_id}/grid"
129

10+
1311
def get_dag_run_url(dag_id: str, dag_run_id: str) -> str:
1412
return f"{AIRFLOW_HOST}/dags/{dag_id}/grid?dag_run_id={dag_run_id}"
1513

14+
1615
def get_task_instance_url(dag_id: str, dag_run_id: str, task_id: str) -> str:
1716
return f"{AIRFLOW_HOST}/dags/{dag_id}/grid?dag_run_id={dag_run_id}&task_id={task_id}"
1817

18+
1919
async def fetch_dags(
2020
limit: int | None = None,
2121
offset: int | None = None,
@@ -44,69 +44,62 @@ async def fetch_dags(
4444

4545
url = f"{AIRFLOW_HOST}{path}"
4646
async with httpx.AsyncClient(follow_redirects=True) as client:
47-
response = await client.get(
48-
url,
49-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
50-
params=params
51-
)
47+
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), params=params)
5248
response.raise_for_status()
5349
data = response.json()
54-
50+
5551
# Add UI links to each DAG
5652
for dag in data.get("dags", []):
5753
dag["ui_url"] = get_dag_url(dag["dag_id"])
58-
54+
5955
return [types.TextContent(type="text", text=response.text)]
6056

57+
6158
async def get_dag(dag_id: str) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
6259
path = f"/api/v1/dags/{dag_id}"
6360
url = f"{AIRFLOW_HOST}{path}"
6461
async with httpx.AsyncClient(follow_redirects=True) as client:
6562
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD))
6663
response.raise_for_status()
6764
data = response.json()
68-
65+
6966
# Add UI link to DAG
7067
data["ui_url"] = get_dag_url(dag_id)
71-
68+
7269
return [types.TextContent(type="text", text=response.text)]
7370

71+
7472
async def pause_dag(dag_id: str) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
7573
path = f"/api/v1/dags/{dag_id}"
7674
url = f"{AIRFLOW_HOST}{path}"
7775
async with httpx.AsyncClient(follow_redirects=True) as client:
7876
response = await client.patch(
79-
url,
80-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
81-
json={"is_paused": True}
77+
url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), json={"is_paused": True}
8278
)
8379
response.raise_for_status()
8480
return [types.TextContent(type="text", text=response.text)]
8581

82+
8683
async def unpause_dag(dag_id: str) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
8784
path = f"/api/v1/dags/{dag_id}"
8885
url = f"{AIRFLOW_HOST}{path}"
8986
async with httpx.AsyncClient(follow_redirects=True) as client:
9087
response = await client.patch(
91-
url,
92-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
93-
json={"is_paused": False}
88+
url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), json={"is_paused": False}
9489
)
9590
response.raise_for_status()
9691
return [types.TextContent(type="text", text=response.text)]
9792

93+
9894
async def trigger_dag(dag_id: str) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
9995
path = f"/api/v1/dags/{dag_id}/dagRuns"
10096
url = f"{AIRFLOW_HOST}{path}"
10197
async with httpx.AsyncClient(follow_redirects=True) as client:
102-
response = await client.post(
103-
url,
104-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
105-
json={}
106-
)
98+
response = await client.post(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), json={})
10799
response.raise_for_status()
108100
return [types.TextContent(type="text", text=response.text)]
109101

102+
110103
async def get_dag_runs(
111104
dag_id: str,
112105
limit: int | None = None,
@@ -151,20 +144,17 @@ async def get_dag_runs(
151144

152145
url = f"{AIRFLOW_HOST}{path}"
153146
async with httpx.AsyncClient(follow_redirects=True) as client:
154-
response = await client.get(
155-
url,
156-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
157-
params=params
158-
)
147+
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), params=params)
159148
response.raise_for_status()
160149
data = response.json()
161-
150+
162151
# Add UI links to each DAG run
163152
for dag_run in data.get("dag_runs", []):
164153
dag_run["ui_url"] = get_dag_run_url(dag_id, dag_run["dag_run_id"])
165-
154+
166155
return [types.TextContent(type="text", text=response.text)]
167156

157+
168158
async def get_dag_tasks(dag_id: str) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
169159
path = f"/api/v1/dags/{dag_id}/tasks"
170160
url = f"{AIRFLOW_HOST}{path}"
@@ -173,14 +163,18 @@ async def get_dag_tasks(dag_id: str) -> list[types.TextContent | types.ImageCont
173163
response.raise_for_status()
174164
return [types.TextContent(type="text", text=response.text)]
175165

176-
async def get_task_instance(dag_id: str, task_id: str, dag_run_id: str) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
166+
167+
async def get_task_instance(
168+
dag_id: str, task_id: str, dag_run_id: str
169+
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
177170
path = f"/api/v1/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}"
178171
url = f"{AIRFLOW_HOST}{path}"
179172
async with httpx.AsyncClient(follow_redirects=True) as client:
180173
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD))
181174
response.raise_for_status()
182175
return [types.TextContent(type="text", text=response.text)]
183176

177+
184178
async def list_task_instances(
185179
dag_id: str,
186180
dag_run_id: str,
@@ -235,22 +229,22 @@ async def list_task_instances(
235229

236230
url = f"{AIRFLOW_HOST}{path}"
237231
async with httpx.AsyncClient(follow_redirects=True) as client:
238-
response = await client.get(
239-
url,
240-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
241-
params=params
242-
)
232+
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), params=params)
243233
response.raise_for_status()
244234
return [types.TextContent(type="text", text=response.text)]
245235

246-
async def get_import_error(import_error_id: int) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
236+
237+
async def get_import_error(
238+
import_error_id: int,
239+
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
247240
path = f"/api/v1/importErrors/{import_error_id}"
248241
url = f"{AIRFLOW_HOST}{path}"
249242
async with httpx.AsyncClient(follow_redirects=True) as client:
250243
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD))
251244
response.raise_for_status()
252245
return [types.TextContent(type="text", text=response.text)]
253246

247+
254248
async def list_import_errors(
255249
limit: int | None = None,
256250
offset: int | None = None,
@@ -267,14 +261,11 @@ async def list_import_errors(
267261

268262
url = f"{AIRFLOW_HOST}{path}"
269263
async with httpx.AsyncClient(follow_redirects=True) as client:
270-
response = await client.get(
271-
url,
272-
auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD),
273-
params=params
274-
)
264+
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD), params=params)
275265
response.raise_for_status()
276266
return [types.TextContent(type="text", text=response.text)]
277267

268+
278269
async def get_health() -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
279270
path = "/api/v1/health"
280271
url = f"{AIRFLOW_HOST}{path}"
@@ -283,10 +274,11 @@ async def get_health() -> list[types.TextContent | types.ImageContent | types.Em
283274
response.raise_for_status()
284275
return [types.TextContent(type="text", text=response.text)]
285276

277+
286278
async def get_version() -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
287279
path = "/api/v1/version"
288280
url = f"{AIRFLOW_HOST}{path}"
289281
async with httpx.AsyncClient(follow_redirects=True) as client:
290282
response = await client.get(url, auth=httpx.BasicAuth(AIRFLOW_USERNAME, AIRFLOW_PASSWORD))
291283
response.raise_for_status()
292-
return [types.TextContent(type="text", text=response.text)]
284+
return [types.TextContent(type="text", text=response.text)]

src/envs.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import os
2+
3+
AIRFLOW_HOST = os.getenv("AIRFLOW_HOST").rstrip("/")
4+
AIRFLOW_USERNAME = os.getenv("AIRFLOW_USERNAME")
5+
AIRFLOW_PASSWORD = os.getenv("AIRFLOW_PASSWORD")

0 commit comments

Comments
 (0)