Skip to content

FastMCP with Hierarchical discovery of tools. #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ jobs:

steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[dev]

- name: Run pytest
run: |
pytest tests/ -v
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ To explicitly start in unsafe mode (though this is default):
airflow-mcp-server --unsafe
```

### Tool Discovery Modes

The server supports two tool discovery approaches:

- **Hierarchical Discovery** (default): Tools are organized by categories (DAGs, Tasks, Connections, etc.). Browse categories first, then select specific tools. More manageable for large APIs.
- **Static Tools** (`--static-tools`): All tools available immediately. Better for programmatic access but can be overwhelming.

To use static tools:
```bash
airflow-mcp-server --static-tools
```

### Considerations

**Authentication**
Expand Down
26 changes: 10 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
[project]
name = "airflow-mcp-server"
version = "0.6.2"
version = "0.7.0"
description = "MCP Server for Airflow"
readme = "README.md"
requires-python = ">=3.11"
authors = [
{name = "Abhishek Bhakat", email = "[email protected]"}
]
dependencies = [
"aiofiles>=24.1.0",
"aiohttp>=3.11.11",
"aioresponses>=0.7.7",
"importlib-resources>=6.5.0",
"mcp>=1.7.1",
"openapi-core>=0.19.4",
"pydantic>=2.11.4",
"pyyaml>=6.0.0",
"packaging>=25.0",
"fastmcp>=2.5.2",
"httpx>=0.28.1",
"click>=8.2.1",
]
classifiers = [
"Development Status :: 3 - Alpha",
Expand All @@ -37,12 +31,12 @@ airflow-mcp-server = "airflow_mcp_server.__main__:main"

[project.optional-dependencies]
dev = [
"build>=1.2.2",
"pre-commit>=4.0.1",
"pytest>=8.3.4",
"pytest-asyncio>=0.25.0",
"pytest-mock>=3.14.0",
"ruff>=0.9.2"
"build>=1.2.2.post1",
"pre-commit>=4.2.0",
"pytest>=8.3.5",
"pytest-asyncio>=1.0.0",
"pytest-mock>=3.14.1",
"ruff>=0.11.12"
]

[build-system]
Expand Down
14 changes: 6 additions & 8 deletions src/airflow_mcp_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
@click.option("-v", "--verbose", count=True, help="Increase verbosity")
@click.option("--safe", "-s", is_flag=True, help="Use only read-only tools")
@click.option("--unsafe", "-u", is_flag=True, help="Use all tools (default)")
@click.option("--static-tools", is_flag=True, help="Use static tools instead of hierarchical discovery")
@click.option("--base-url", help="Airflow API base URL")
@click.option("--auth-token", help="Authentication token (JWT)")
def main(verbose: int, safe: bool, unsafe: bool, base_url: str = None, auth_token: str = None) -> None:
def main(verbose: int, safe: bool, unsafe: bool, static_tools: bool, base_url: str = None, auth_token: str = None) -> None:
"""MCP server for Airflow"""
logging_level = logging.WARN
if verbose == 1:
Expand All @@ -26,26 +27,23 @@ def main(verbose: int, safe: bool, unsafe: bool, base_url: str = None, auth_toke

logging.basicConfig(level=logging_level, stream=sys.stderr)

# Read environment variables with proper precedence
config_base_url = os.environ.get("AIRFLOW_BASE_URL") or base_url
config_auth_token = os.environ.get("AUTH_TOKEN") or auth_token

# Initialize configuration
try:
config = AirflowConfig(base_url=config_base_url, auth_token=config_auth_token)
except ValueError as e:
click.echo(f"Configuration error: {e}", err=True)
sys.exit(1)

# Determine server mode with proper precedence
if safe and unsafe:
raise click.UsageError("Options --safe and --unsafe are mutually exclusive")
elif safe:
# CLI argument for safe mode
asyncio.run(serve_safe(config))
asyncio.run(serve_safe(config, static_tools=static_tools))
elif unsafe:
asyncio.run(serve_unsafe(config, static_tools=static_tools))
else:
# Default to unsafe mode
asyncio.run(serve_unsafe(config))
asyncio.run(serve_unsafe(config, static_tools=static_tools))


if __name__ == "__main__":
Expand Down
3 changes: 0 additions & 3 deletions src/airflow_mcp_server/client/__init__.py

This file was deleted.

163 changes: 0 additions & 163 deletions src/airflow_mcp_server/client/airflow_client.py

This file was deleted.

Loading