diff --git a/MetasploitMCP.py b/MetasploitMCP.py index 16a874c..573f3b9 100644 --- a/MetasploitMCP.py +++ b/MetasploitMCP.py @@ -1389,10 +1389,7 @@ app = FastAPI( # Setup MCP transport (SSE for HTTP mode) sse = SseServerTransport("/messages/") -# Manually add the route for POST to /messages/ (needed for FastMCP+FastAPI) -app.router.routes.append(Mount("/messages", app=Starlette(routes=[ - Route("/", endpoint=sse.handle_post_message, methods=["POST"]) -]))) +# DO NOT manually add routes for /messages - it's handled internally by SseServerTransport @app.get("/sse", tags=["MCP"]) async def handle_sse_connection(request: Request): @@ -1444,34 +1441,39 @@ if __name__ == "__main__": logger.critical(f"CRITICAL: Failed to initialize Metasploit client on startup: {e}. Server cannot function.") sys.exit(1) # Exit if MSF connection fails at start - # Determine launch mode (stdio for Claude Desktop, HTTP otherwise) - is_claude_stdio = hasattr(sys.stdin, 'isatty') and not sys.stdin.isatty() + # --- Setup argument parser for transport mode and server configuration --- + import argparse + + parser = argparse.ArgumentParser(description='Run Streamlined Metasploit MCP Server') + parser.add_argument( + '--transport', + choices=['http', 'stdio'], + default='http', + help='MCP transport mode to use (http=SSE, stdio=direct pipe)' + ) + parser.add_argument('--host', default='127.0.0.1', help='Host to bind the HTTP server to (default: 127.0.0.1)') + parser.add_argument('--port', type=int, default=None, help='Port to listen on (default: find available from 8085)') + parser.add_argument('--reload', action='store_true', help='Enable auto-reload (for development)') + parser.add_argument('--find-port', action='store_true', help='Force finding an available port starting from --port or 8085') + args = parser.parse_args() - if is_claude_stdio: - logger.info("Detected non-interactive stdin. Assuming Claude Desktop launch. Using stdio transport.") - # Run MCP server over stdio (synchronous call) + if args.transport == 'stdio': + logger.info("Starting MCP server in STDIO transport mode.") try: - mcp.run(transport="stdio") # FastMCP handles async tools within its run loop + mcp.run(transport="stdio") except Exception as e: - logger.exception("Error during MCP stdio run loop.") - sys.exit(1) + logger.exception("Error during MCP stdio run loop.") + sys.exit(1) logger.info("MCP stdio server finished.") - else: - logger.info("Detected interactive terminal or non-stdio launch. Starting HTTP server.") - # --- HTTP Server Setup --- - import argparse - - parser = argparse.ArgumentParser(description='Run Streamlined Metasploit MCP Server (HTTP Mode)') - parser.add_argument('--host', default='127.0.0.1', help='Host to bind the HTTP server to (default: 127.0.0.1)') - parser.add_argument('--port', type=int, default=None, help='Port to listen on (default: find available from 8085)') - parser.add_argument('--reload', action='store_true', help='Enable auto-reload (for development)') - parser.add_argument('--find-port', action='store_true', help='Force finding an available port starting from --port or 8085') - args = parser.parse_args() - + else: # HTTP/SSE mode (default) + logger.info("Starting MCP server in HTTP/SSE transport mode.") + + # Check port availability + check_host = args.host if args.host != '0.0.0.0' else '127.0.0.1' selected_port = args.port if selected_port is None or args.find_port: start_port = selected_port if selected_port is not None else 8085 - selected_port = find_available_port(start_port, host=args.host) + selected_port = find_available_port(start_port, host=check_host) logger.info(f"Starting Uvicorn HTTP server on http://{args.host}:{selected_port}") logger.info(f"MCP SSE Endpoint: /sse") @@ -1480,9 +1482,9 @@ if __name__ == "__main__": logger.info(f"Auto-reload: {'Enabled' if args.reload else 'Disabled'}") uvicorn.run( - "__main__:app", # Point to the app object in the current file + "__main__:app", host=args.host, port=selected_port, reload=args.reload, - log_level="info" # Use Uvicorn's logging for server events + log_level="info" ) \ No newline at end of file diff --git a/README.md b/README.md index fb800eb..d1a7633 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,30 @@ Start the Metasploit RPC service: msfrpcd -P yourpassword -S -a 127.0.0.1 -p 55553 ``` +### Transport Options + +The server supports two transport methods: + +- **HTTP/SSE (Server-Sent Events)**: Default mode for interoperability with most MCP clients +- **STDIO (Standard Input/Output)**: Used with Claude Desktop and similar direct pipe connections + +You can explicitly select the transport mode using the `--transport` flag: + +```bash +# Run with HTTP/SSE transport (default) +python MetasploitMCP.py --transport http + +# Run with STDIO transport +python MetasploitMCP.py --transport stdio +``` + +Additional options for HTTP mode: +```bash +python MetasploitMCP.py --transport http --host 0.0.0.0 --port 8085 +``` + +### Claude Desktop Integration + For Claude Desktop integration, configure `claude_desktop_config.json`: ```json @@ -76,7 +100,9 @@ For Claude Desktop integration, configure `claude_desktop_config.json`: "--directory", "C:\\path\\to\\MetasploitMCP", "run", - "MetasploitMCP.py" + "MetasploitMCP.py", + "--transport", + "stdio" ], "env": { "MSF_PASSWORD": "yourpassword" @@ -86,6 +112,18 @@ For Claude Desktop integration, configure `claude_desktop_config.json`: } ``` +### Other MCP Clients + +For other MCP clients that use HTTP/SSE: + +1. Start the server in HTTP mode: + ```bash + python MetasploitMCP.py --transport http --host 0.0.0.0 --port 8085 + ``` + +2. Configure your MCP client to connect to: + - SSE endpoint: `http://your-server-ip:8085/sse` + ## Security Considerations ⚠️ **IMPORTANT SECURITY WARNING**: