From f93c66caf0523b21b1bb23b7e5e8c31f5b0278bd Mon Sep 17 00:00:00 2001 From: olly-reardon Date: Fri, 12 Dec 2025 11:46:56 -0500 Subject: [PATCH 1/2] feat: add configurable transport support (stdio/sse/streamable-http) --- src/fleet_mcp/config.py | 5 +++++ src/fleet_mcp/server.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/fleet_mcp/config.py b/src/fleet_mcp/config.py index a538732..2380170 100644 --- a/src/fleet_mcp/config.py +++ b/src/fleet_mcp/config.py @@ -61,6 +61,11 @@ class FleetConfig(BaseSettings): description="Number of hours to retain completed async query results before cleanup", ) + transport: str = Field( + default="stdio", + description="MCP transport protocol: 'stdio', 'sse', or 'streamable-http'", + ) + @field_validator("server_url") @classmethod def validate_server_url(cls, v: str) -> str: diff --git a/src/fleet_mcp/server.py b/src/fleet_mcp/server.py index 682ade6..89ecdac 100644 --- a/src/fleet_mcp/server.py +++ b/src/fleet_mcp/server.py @@ -447,11 +447,12 @@ def run(self) -> None: import asyncio logger.info(f"Starting Fleet MCP Server for {self.config.server_url}") + logger.info(f"Transport: {self.config.transport}") # Preload schema cache before starting server asyncio.run(self._preload_schema_cache()) - self.mcp.run() + self.mcp.run(transport=self.config.transport) def create_server(config: FleetConfig | None = None) -> FleetMCPServer: From bae04c324f9367fddb8814983bc425ec7bf1b22f Mon Sep 17 00:00:00 2001 From: olly-reardon Date: Fri, 12 Dec 2025 12:55:11 -0500 Subject: [PATCH 2/2] modify server and cli to add additional features --- src/fleet_mcp/cli.py | 18 ++++++++++++++++-- src/fleet_mcp/server.py | 24 +++++++++++++++++++----- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/fleet_mcp/cli.py b/src/fleet_mcp/cli.py index fc89910..867e604 100644 --- a/src/fleet_mcp/cli.py +++ b/src/fleet_mcp/cli.py @@ -84,19 +84,33 @@ def _load_config(ctx: click.Context) -> FleetConfig: sys.exit(1) + @cli.command() +@click.option( + "--port", + "-p", + type=int, + default=None, + help="Port to listen on for HTTP transports (default: 8000, env: FLEET_MCP_PORT)", + show_default=False, +) @click.pass_context -def run(ctx: click.Context) -> None: +def run(ctx: click.Context, port: int | None = None) -> None: """Run the Fleet MCP server.""" config = _load_config(ctx) + # Dynamically determine port: CLI > env > default + if port is None: + import os + port = int(os.environ.get("FLEET_MCP_PORT", 8000)) + try: server = FleetMCPServer(config) readonly_status = " (READ-ONLY MODE)" if config.readonly else "" click.echo( f"Starting Fleet MCP Server for {config.server_url}{readonly_status}" ) - server.run() + server.run(port=port) except KeyboardInterrupt: click.echo("\nShutting down Fleet MCP Server...") except Exception as e: diff --git a/src/fleet_mcp/server.py b/src/fleet_mcp/server.py index 89ecdac..6cf68e4 100644 --- a/src/fleet_mcp/server.py +++ b/src/fleet_mcp/server.py @@ -1,6 +1,9 @@ """Fleet MCP Server - Main MCP server implementation.""" + import logging +import os +import argparse from typing import Any from mcp.server.fastmcp import FastMCP @@ -442,17 +445,24 @@ async def _preload_schema_cache(self) -> None: logger.warning(f"Failed to preload schema cache: {e}") logger.info("Schema cache will be loaded on first use") - def run(self) -> None: - """Run the MCP server.""" + def run(self, port: int = 8000) -> None: + """Run the MCP server, supporting custom port.""" import asyncio + import uvicorn logger.info(f"Starting Fleet MCP Server for {self.config.server_url}") logger.info(f"Transport: {self.config.transport}") + logger.info(f"Listening on port: {port}") # Preload schema cache before starting server asyncio.run(self._preload_schema_cache()) - self.mcp.run(transport=self.config.transport) + # Only pass port for HTTP-based transports + if self.config.transport in ("sse", "streamable-http"): + app = self.mcp.sse_app() if self.config.transport == "sse" else self.mcp.streamable_http_app() + uvicorn.run(app, host="0.0.0.0", port=port, log_level="info") + else: + self.mcp.run(transport=self.config.transport) def create_server(config: FleetConfig | None = None) -> FleetMCPServer: @@ -468,16 +478,20 @@ def create_server(config: FleetConfig | None = None) -> FleetMCPServer: def main() -> None: - """Main entry point for Fleet MCP Server.""" + """Main entry point for Fleet MCP Server with port support.""" # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) + parser = argparse.ArgumentParser(description="Fleet MCP Server") + parser.add_argument("--port", "-p", type=int, default=int(os.environ.get("FLEET_MCP_PORT", 8000)), help="Port to listen on for HTTP transports (default: 8000, env: FLEET_MCP_PORT)") + args, unknown = parser.parse_known_args() + try: server = create_server() - server.run() + server.run(port=args.port) except Exception as e: logger.error(f"Failed to start Fleet MCP Server: {e}") raise