forked from konflux-ci/konflux-devlake-mcp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkonflux-devlake-mcp.py
More file actions
257 lines (219 loc) · 8.04 KB
/
Copy pathkonflux-devlake-mcp.py
File metadata and controls
257 lines (219 loc) · 8.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/usr/bin/env python3
"""
Konflux DevLake MCP Server - Main Entry Point
MCP server providing natural language access to Konflux DevLake databases.
Supports incident analysis, deployment tracking, and database operations.
Features:
- HTTP/STDIO transport protocols
- Incident analysis with filtering and deduplication
- Deployment tracking and analytics
- Database connectivity and querying
- SQL injection protection
- Comprehensive logging
Usage:
python konflux-devlake-mcp.py --transport http --host 0.0.0.0 --port 3000 \\
--db-host localhost --db-user root --db-password password \\
--db-database lake
"""
import sys
import os
import asyncio
import logging
import argparse
from server.factory.server_factory import ServerFactory
from utils.config import KonfluxDevLakeConfig
from utils.logger import get_logger, log_system_info, shutdown_logging
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, current_dir)
def create_parser() -> argparse.ArgumentParser:
"""
Create command-line argument parser with all server configuration options.
Returns:
Configured ArgumentParser with transport, server, database, and logging options
"""
parser = argparse.ArgumentParser(
description="Konflux DevLake MCP Server - Database Querying Interface",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
HTTP Mode (Production):
python konflux-devlake-mcp.py --transport http --host 0.0.0.0 --port 3000 \
--db-host localhost --db-user root --db-password password --db-database lake
STDIO Mode (Development):
python konflux-devlake-mcp.py --transport stdio --db-host localhost \
--db-user root --db-password password
Debug Mode:
python konflux-devlake-mcp.py --transport http --log-level DEBUG \
--db-host localhost --db-user root --db-password password
""",
)
parser.add_argument(
"--transport",
type=str,
choices=["stdio", "http"],
default="http",
help="Transport protocol: 'stdio' for local development (direct communication), "
"'http' for production server (network accessible)",
)
parser.add_argument(
"--host",
type=str,
default="0.0.0.0",
help="HTTP server host address (default: 0.0.0.0 for all network interfaces). "
"Use '127.0.0.1' for localhost only, '0.0.0.0' for all interfaces",
)
parser.add_argument(
"--port",
type=int,
default=3000,
help="HTTP server port number (default: 3000). Must be available and not in use. "
"Common alternatives: 8080, 8000, 5000",
)
# Database Connection Configuration
parser.add_argument(
"--db-host",
type=str,
default="localhost",
help="Database server hostname or IP address (default: localhost). "
"For remote databases, use the actual server address",
)
parser.add_argument(
"--db-port",
type=int,
default=3306,
help="Database server port number (default: 3306 for MySQL). "
"Use 5432 for PostgreSQL, 1433 for SQL Server",
)
parser.add_argument(
"--db-user",
type=str,
default="root",
help="Database username for authentication (default: root). "
"Must have appropriate permissions to access DevLake tables",
)
parser.add_argument(
"--db-password",
type=str,
default="",
help="Database password for authentication (default: empty). "
"For security, consider using environment variables or config files",
)
parser.add_argument(
"--db-database",
type=str,
default="",
help="Default database name to connect to (default: empty). "
"Usually 'lake' for DevLake installations. Leave empty for auto-detection",
)
# Logging Configuration
parser.add_argument(
"--log-level",
type=str,
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
default="INFO",
help="Logging verbosity level (default: INFO). "
"DEBUG: Detailed debug info, INFO: General operations, "
"WARNING: Only warnings and errors, ERROR: Only errors",
)
return parser
def create_config(args: argparse.Namespace) -> KonfluxDevLakeConfig:
"""
Create server configuration from command-line arguments.
Args:
args: Parsed command-line arguments
Returns:
KonfluxDevLakeConfig object with all server settings
"""
config = KonfluxDevLakeConfig()
config.database.host = args.db_host
config.database.port = args.db_port
config.database.user = args.db_user
config.database.password = args.db_password
config.database.database = args.db_database
config.server.transport = args.transport
config.server.host = args.host
config.server.port = args.port
config.logging.level = args.log_level
logging.getLogger().setLevel(getattr(logging, config.logging.level))
return config
def validate_config(config: KonfluxDevLakeConfig) -> bool:
"""
Validate server configuration for required settings.
"""
logger = get_logger(__name__)
if not config.database.host:
logger.error("Database host is required - specify with --db-host")
return False
if not config.database.user:
logger.error("Database user is required - specify with --db-user")
return False
logger.info("Configuration validation passed")
return True
async def run_server(config: KonfluxDevLakeConfig) -> int:
"""
Start and run the MCP server with the given configuration.
Creates server factory, initializes transport layer, and starts the server.
Handles graceful shutdown on interruption.
"""
logger = get_logger(__name__)
server_factory = ServerFactory()
server = None
transport = None
try:
logger.info("Starting Konflux DevLake MCP Server")
log_system_info()
server = server_factory.create_server(config)
transport_kwargs = (
{
"host": config.server.host,
"port": config.server.port,
"timeout_keep_alive": config.server.timeout_keep_alive,
"timeout_graceful_shutdown": config.server.timeout_graceful_shutdown,
"config": config,
}
if config.server.transport == "http"
else {}
)
transport = server_factory.create_transport(config.server.transport, **transport_kwargs)
logger.info(f"Server created with {config.server.transport} transport")
await server.start(transport)
return 0
except (KeyboardInterrupt, asyncio.CancelledError):
logger.info("Received shutdown signal")
return 0
except Exception as e:
logger.error(f"Server runtime error: {e}")
return 1
finally:
logger.info("Shutting down server")
try:
if "server" in locals() and server:
await server.shutdown()
if "transport" in locals() and transport:
await transport.stop()
shutdown_logging()
logger.info("Server shutdown complete")
except (asyncio.CancelledError, KeyboardInterrupt):
# Ignore cancellation errors during shutdown
pass
except (ValueError, OSError):
# Ignore I/O errors during shutdown (handlers may be closed)
pass
except Exception as e:
# Only log non-I/O errors during shutdown
try:
logger.error(f"Error during shutdown: {e}")
except (ValueError, OSError):
# If logging fails, just pass silently
pass
async def main():
"""Main application entry point."""
parser = create_parser()
args = parser.parse_args()
config = create_config(args)
if not validate_config(config):
return 1
return await run_server(config)
if __name__ == "__main__":
exit_code = asyncio.run(main())
sys.exit(exit_code)