|
2 | 2 | import os |
3 | 3 | from dataclasses import dataclass |
4 | 4 | from typing import Any, Dict, Optional, List |
5 | | -import sys |
6 | 5 |
|
7 | | -import dotenv |
8 | | -import requests |
| 6 | +from mcp.server import Server |
9 | 7 | from mcp.server.fastmcp import FastMCP |
| 8 | +from mcp.server.sse import SseServerTransport |
10 | 9 | from requests.compat import urljoin |
| 10 | +from starlette.applications import Starlette |
| 11 | +from starlette.requests import Request |
| 12 | +from starlette.routing import Mount, Route |
| 13 | +import dotenv |
| 14 | +import requests |
| 15 | +import uvicorn |
11 | 16 |
|
12 | 17 | dotenv.load_dotenv() |
13 | 18 | mcp = FastMCP("Alertmanager MCP") |
@@ -279,17 +284,90 @@ def setup_environment(): |
279 | 284 | return True |
280 | 285 |
|
281 | 286 |
|
| 287 | +def create_starlette_app(mcp_server: Server, *, debug: bool = False) -> Starlette: |
| 288 | + """Create a Starlette application that can serve the provided MCP server with SSE. |
| 289 | +
|
| 290 | + Sets up a Starlette web application with routes for SSE (Server-Sent Events) |
| 291 | + communication with the MCP server. |
| 292 | +
|
| 293 | + Args: |
| 294 | + mcp_server: The MCP server instance to connect |
| 295 | + debug: Whether to enable debug mode for the Starlette app |
| 296 | +
|
| 297 | + Returns: |
| 298 | + A configured Starlette application |
| 299 | + """ |
| 300 | + # Create an SSE transport with a base path for messages |
| 301 | + sse = SseServerTransport("/messages/") |
| 302 | + |
| 303 | + async def handle_sse(request: Request) -> None: |
| 304 | + """Handler for SSE connections. |
| 305 | +
|
| 306 | + Establishes an SSE connection and connects it to the MCP server. |
| 307 | +
|
| 308 | + Args: |
| 309 | + request: The incoming HTTP request |
| 310 | + """ |
| 311 | + # Connect the SSE transport to the request |
| 312 | + async with sse.connect_sse( |
| 313 | + request.scope, |
| 314 | + request.receive, |
| 315 | + request._send, # noqa: SLF001 |
| 316 | + ) as (read_stream, write_stream): |
| 317 | + # Run the MCP server with the SSE streams |
| 318 | + await mcp_server.run( |
| 319 | + read_stream, |
| 320 | + write_stream, |
| 321 | + mcp_server.create_initialization_options(), |
| 322 | + ) |
| 323 | + |
| 324 | + # Create and return the Starlette application with routes |
| 325 | + return Starlette( |
| 326 | + debug=debug, |
| 327 | + routes=[ |
| 328 | + Route("/sse", endpoint=handle_sse), # Endpoint for SSE connections |
| 329 | + # Endpoint for posting messages |
| 330 | + Mount("/messages/", app=sse.handle_post_message), |
| 331 | + ], |
| 332 | + ) |
| 333 | + |
| 334 | + |
282 | 335 | def run_server(): |
283 | 336 | """Main entry point for the Prometheus Alertmanager MCP Server""" |
284 | | - # Setup environment |
285 | | - if not setup_environment(): |
286 | | - sys.exit(1) |
287 | | - |
| 337 | + setup_environment() |
| 338 | + # Get the underlying MCP server from the FastMCP instance |
| 339 | + mcp_server = mcp._mcp_server # noqa: WPS437 |
| 340 | + |
| 341 | + import argparse |
| 342 | + |
| 343 | + # Set up command-line argument parsing |
| 344 | + parser = argparse.ArgumentParser( |
| 345 | + description='Run MCP server with configurable transport') |
| 346 | + # Allow choosing between stdio and SSE transport modes |
| 347 | + parser.add_argument('--transport', choices=['stdio', 'sse'], default='stdio', |
| 348 | + help='Transport mode (stdio or sse)') |
| 349 | + # Host configuration for SSE mode |
| 350 | + parser.add_argument('--host', default='0.0.0.0', |
| 351 | + help='Host to bind to (for SSE mode)') |
| 352 | + # Port configuration for SSE mode |
| 353 | + parser.add_argument('--port', type=int, default=8000, |
| 354 | + help='Port to listen on (for SSE mode)') |
| 355 | + args = parser.parse_args() |
288 | 356 | print("\nStarting Prometheus Alertmanager MCP Server...") |
289 | | - print("Running server in standard mode...") |
290 | 357 |
|
291 | | - # Run the server with the stdio transport |
292 | | - mcp.run(transport="stdio") |
| 358 | + # Launch the server with the selected transport mode |
| 359 | + if args.transport == 'stdio': |
| 360 | + print("Running server with stdio transport (default)") |
| 361 | + # Run with stdio transport (default) |
| 362 | + # This mode communicates through standard input/output |
| 363 | + mcp.run(transport='stdio') |
| 364 | + else: |
| 365 | + print("Running server with SSE transport (web-based)") |
| 366 | + # Run with SSE transport (web-based) |
| 367 | + # Create a Starlette app to serve the MCP server |
| 368 | + starlette_app = create_starlette_app(mcp_server, debug=True) |
| 369 | + # Start the web server with the configured host and port |
| 370 | + uvicorn.run(starlette_app, host=args.host, port=args.port) |
293 | 371 |
|
294 | 372 |
|
295 | 373 | if __name__ == "__main__": |
|
0 commit comments