astro-airflow-mcp 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ """Airflow MCP Server."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ __version__ = version("astro-airflow-mcp")
@@ -0,0 +1,75 @@
1
+ """Main entry point for running the Airflow MCP server."""
2
+
3
+ import argparse
4
+ import logging
5
+ import os
6
+
7
+ from astro_airflow_mcp.logging import configure_logging, get_logger
8
+ from astro_airflow_mcp.server import configure, mcp
9
+
10
+ logger = get_logger("main")
11
+
12
+
13
+ def main():
14
+ """Main entry point for the Airflow MCP server."""
15
+ # Configure logging
16
+ configure_logging(level=logging.INFO)
17
+
18
+ # Parse command line arguments
19
+ parser = argparse.ArgumentParser(description="Airflow MCP Server")
20
+ parser.add_argument(
21
+ "--transport",
22
+ type=str,
23
+ default=os.getenv("MCP_TRANSPORT", "http"),
24
+ choices=["stdio", "http"],
25
+ help="Transport mode: http (default) or stdio",
26
+ )
27
+ parser.add_argument(
28
+ "--host",
29
+ type=str,
30
+ default=os.getenv("MCP_HOST", "localhost"),
31
+ help="Host to bind to (only for http transport, default: localhost)",
32
+ )
33
+ parser.add_argument(
34
+ "--port",
35
+ type=int,
36
+ default=int(os.getenv("MCP_PORT", "8000")),
37
+ help="Port to bind to (only for http transport, default: 8000)",
38
+ )
39
+ parser.add_argument(
40
+ "--airflow-url",
41
+ type=str,
42
+ default=os.getenv("AIRFLOW_API_URL", "http://localhost:8080"),
43
+ help="Base URL of Airflow webserver (default: http://localhost:8080)",
44
+ )
45
+ parser.add_argument(
46
+ "--auth-token",
47
+ type=str,
48
+ default=os.getenv("AIRFLOW_AUTH_TOKEN"),
49
+ help="Bearer token for Airflow API authentication",
50
+ )
51
+
52
+ args = parser.parse_args()
53
+
54
+ # Configure Airflow connection settings
55
+ configure(
56
+ url=args.airflow_url,
57
+ auth_token=args.auth_token,
58
+ )
59
+
60
+ # Log Airflow connection configuration
61
+ logger.info(f"Airflow URL: {args.airflow_url}")
62
+ if args.auth_token:
63
+ logger.info("Authentication: Bearer token")
64
+ else:
65
+ logger.info("Authentication: None")
66
+
67
+ # Run the server with specified transport
68
+ if args.transport == "http":
69
+ mcp.run(transport="http", host=args.host, port=args.port, show_banner=False)
70
+ else:
71
+ mcp.run(show_banner=False)
72
+
73
+
74
+ if __name__ == "__main__":
75
+ main()
@@ -0,0 +1,61 @@
1
+ """Logging utilities for Airflow MCP."""
2
+
3
+ import logging
4
+ import sys
5
+
6
+
7
+ def get_logger(name: str | None = None) -> logging.Logger:
8
+ """Get a logger instance nested under the airflow_mcp namespace.
9
+
10
+ Args:
11
+ name: Optional name for the logger. If not provided, returns root airflow_mcp logger.
12
+ Will be nested under 'airflow_mcp' (e.g., 'airflow_mcp.server')
13
+
14
+ Returns:
15
+ A logger instance
16
+
17
+ Example:
18
+ >>> logger = get_logger("server")
19
+ >>> logger.info("Starting server")
20
+ """
21
+ if name:
22
+ return logging.getLogger(f"airflow_mcp.{name}")
23
+ return logging.getLogger("airflow_mcp")
24
+
25
+
26
+ def configure_logging(level: str | int = logging.INFO) -> None:
27
+ """Configure logging for the airflow_mcp package.
28
+
29
+ Sets up a simple console handler with a standard format.
30
+
31
+ Args:
32
+ level: Logging level (e.g., logging.INFO, logging.DEBUG, or "INFO", "DEBUG")
33
+ Defaults to INFO.
34
+
35
+ Example:
36
+ >>> configure_logging(level=logging.DEBUG)
37
+ """
38
+ # Convert string level to int if needed
39
+ if isinstance(level, str):
40
+ level = getattr(logging, level.upper(), logging.INFO)
41
+
42
+ # Get the root airflow_mcp logger
43
+ logger = get_logger()
44
+ logger.setLevel(level)
45
+
46
+ # Remove any existing handlers to avoid duplicates
47
+ logger.handlers.clear()
48
+
49
+ # Create console handler
50
+ handler = logging.StreamHandler(sys.stdout)
51
+ handler.setLevel(level)
52
+
53
+ # Create simple formatter
54
+ formatter = logging.Formatter(fmt="%(levelname)s: %(message)s")
55
+ handler.setFormatter(formatter)
56
+
57
+ # Add handler to logger
58
+ logger.addHandler(handler)
59
+
60
+ # Prevent propagation to root logger to avoid duplicate logs
61
+ logger.propagate = False
@@ -0,0 +1,66 @@
1
+ """Airflow plugin for integrating MCP server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any
7
+
8
+ from astro_airflow_mcp import __version__
9
+
10
+ # Use standard logging for Airflow plugin integration
11
+ # This allows Airflow to control log level, format, and destination
12
+ logger = logging.getLogger(__name__)
13
+
14
+ try:
15
+ from airflow.plugins_manager import AirflowPlugin
16
+
17
+ AIRFLOW_AVAILABLE = True
18
+ except ImportError:
19
+ AIRFLOW_AVAILABLE = False
20
+ AirflowPlugin = object # type: ignore
21
+ logger.warning("Airflow not available, plugin disabled")
22
+
23
+
24
+ # FastAPI app configuration for Airflow 3.x plugin system
25
+ try:
26
+ from fastapi import FastAPI
27
+
28
+ from astro_airflow_mcp.server import mcp
29
+
30
+ # Get the native MCP protocol ASGI app from FastMCP
31
+ mcp_protocol_app = mcp.http_app(path="/")
32
+
33
+ # Wrap in a FastAPI app with the MCP app's lifespan
34
+ # This is required for FastMCP to initialize its task group
35
+ app = FastAPI(
36
+ title="Airflow MCP Server", version=__version__, lifespan=mcp_protocol_app.lifespan
37
+ )
38
+
39
+ # Mount the MCP protocol app
40
+ app.mount("/v1", mcp_protocol_app)
41
+ logger.info("MCP protocol app created and mounted")
42
+
43
+ # Airflow plugin configuration
44
+ fastapi_apps_config = [{"app": app, "url_prefix": "/mcp", "name": "Airflow MCP Server"}]
45
+
46
+ except ImportError as e:
47
+ logger.warning(f"FastAPI integration not available: {e}")
48
+ fastapi_apps_config = []
49
+
50
+
51
+ class AirflowMCPPlugin(AirflowPlugin):
52
+ """Plugin to integrate MCP server with Airflow.
53
+
54
+ Exposes MCP protocol endpoints at /mcp for AI clients (Cursor, Claude Desktop, etc.)
55
+ """
56
+
57
+ name = "astro_airflow_mcp"
58
+ fastapi_apps = fastapi_apps_config
59
+
60
+ @staticmethod
61
+ def on_load(*_args: Any, **_kwargs: Any) -> None:
62
+ """Called when the plugin is loaded."""
63
+ logger.info("Airflow MCP Plugin loaded")
64
+
65
+
66
+ __all__ = ["AirflowMCPPlugin"]