From 94e8ad16c73a5aeecbca18a1f1958e04e5c01542 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Mon, 29 Sep 2025 07:23:01 -0700 Subject: [PATCH 1/3] Add Streamable HTTP Transport Quick and dirty update to add HTTP Streamable output that works with the latest versions of FastMCP. There is no authentication, so this is just a start and should be used for development/testing. This should also fix SSE transport, but this has not been tested. --- multi_ssh_mcp.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/multi_ssh_mcp.py b/multi_ssh_mcp.py index 8a3a497..b88c2e7 100644 --- a/multi_ssh_mcp.py +++ b/multi_ssh_mcp.py @@ -426,6 +426,7 @@ def main(): # Create FastMCP server mcp = FastMCP("Multi-SSH Server") + @mcp.tool() def list_servers() -> str: """List all configured SSH servers with their details""" @@ -752,19 +753,30 @@ def network_diagnostics(command_type: str, destination: str, server_name: str = return f"{command_type} failed: {result['error']}" # Run the FastMCP server with transport selection - transport = os.environ.get("MCP_TRANSPORT", "stdio") + transport = os.environ.get("MCP_TRANSPORT", "stdio").lower() if transport == "sse": - # Server-Sent Events mode for HTTP streaming + # Server-Sent Events mode for HTTP streaming (not recommended) import uvicorn - from fastmcp.sse import create_sse_transport + sse_transport = mcp.sse_app() port = int(os.environ.get("MCP_PORT", "8080")) host = os.environ.get("MCP_HOST", "0.0.0.0") logger.info(f"Starting SSE transport on {host}:{port}") - sse_transport = create_sse_transport(mcp, host=host, port=port) uvicorn.run(sse_transport, host=host, port=port, log_level="info") + + elif transport == "http": + # Streamable HTTP transport (recommended) + import uvicorn + http_transport = mcp.http_app() + + port = int(os.environ.get("MCP_PORT", "8080")) + host = os.environ.get("MCP_HOST", "0.0.0.0") + + logger.info(f"Starting Streamable HTTP transport on {host}:{port}") + uvicorn.run(http_transport, host=host, port=port, log_level="info") + else: # Default stdio transport logger.info("Starting stdio transport") @@ -772,4 +784,4 @@ def network_diagnostics(command_type: str, destination: str, server_name: str = if __name__ == "__main__": - main() \ No newline at end of file + main() From d229645a4249a962958327f269c198acb3d4fcc3 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 17 Oct 2025 09:29:50 -0700 Subject: [PATCH 2/3] Add simple token authorization Added simple Bearer preshared token authorization using the MCP_TOKEN environment variable --- multi_ssh_mcp.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/multi_ssh_mcp.py b/multi_ssh_mcp.py index b88c2e7..40efda4 100644 --- a/multi_ssh_mcp.py +++ b/multi_ssh_mcp.py @@ -20,6 +20,7 @@ import paramiko from fastmcp import FastMCP +from fastmcp.server.auth.providers.jwt import StaticTokenVerifier import jc # Import security utilities @@ -424,7 +425,18 @@ def main(): logger.info(f"Loaded {len(ssh_manager.servers_config)} server(s): {', '.join(ssh_manager.servers_config.keys())}") # Create FastMCP server - mcp = FastMCP("Multi-SSH Server") + bearer_token = os.environ.get("MCP_TOKEN", "stdio") + verifier = StaticTokenVerifier( + tokens={ + bearer_token: { + "client_id": "helios@juniper.net", + "scopes": ["read:data", "write:data", "admin:users"] + } + }, + required_scopes=["read:data"] + ) + + mcp = FastMCP("Multi-SSH Server", auth=verifier) @mcp.tool() From 825361542786270ac089ef3e721bd31ea7ec27b1 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 17 Oct 2025 09:36:27 -0700 Subject: [PATCH 3/3] fix auth logic - option for no authentication --- multi_ssh_mcp.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/multi_ssh_mcp.py b/multi_ssh_mcp.py index 40efda4..1ad71de 100644 --- a/multi_ssh_mcp.py +++ b/multi_ssh_mcp.py @@ -425,19 +425,22 @@ def main(): logger.info(f"Loaded {len(ssh_manager.servers_config)} server(s): {', '.join(ssh_manager.servers_config.keys())}") # Create FastMCP server - bearer_token = os.environ.get("MCP_TOKEN", "stdio") - verifier = StaticTokenVerifier( - tokens={ - bearer_token: { - "client_id": "helios@juniper.net", - "scopes": ["read:data", "write:data", "admin:users"] - } - }, - required_scopes=["read:data"] - ) - - mcp = FastMCP("Multi-SSH Server", auth=verifier) + bearer_token = os.environ.get("MCP_TOKEN") + + if bearer_token: + verifier = StaticTokenVerifier( + tokens={ + bearer_token: { + "client_id": "superuser", + "scopes": ["read:data", "write:data", "admin:users"] + } + }, + required_scopes=["read:data"] + ) + mcp = FastMCP("Multi-SSH Server", auth=verifier) + else: + mcp = FastMCP("Multi-SSH Server") @mcp.tool() def list_servers() -> str: