"""
Entry point for running the MT5 MCP server as a module.
Supports multiple transport methods:
- stdio: Local desktop clients (Claude Desktop, VS Code) - unlimited (default)
- http: Remote/web clients via Gradio - rate limited
- both: Run stdio and HTTP simultaneously when explicitly requested
"""
import argparse
import asyncio
import logging
from threading import Thread
logger = logging.getLogger(__name__)
async def run_stdio_server():
"""Run stdio MCP server (existing stdio implementation)."""
from mcp.server.stdio import stdio_server
from mcp.server.models import InitializationOptions
from mcp.server import NotificationOptions
from .server import app
from .connection import get_connection
logger.info("Starting stdio MCP server...")
# Initialize MT5 connection
try:
get_connection()
logger.info("MT5 connection established successfully")
except Exception as e:
logger.error(f"Failed to initialize MT5 connection: {e}")
raise
# Run the server using stdio transport
async with stdio_server() as (read_stream, write_stream):
logger.info("stdio server initialized, waiting for requests...")
init_options = InitializationOptions(
server_name="metatrader5-mcp",
server_version="0.5.2",
capabilities=app.get_capabilities(
notification_options=NotificationOptions(), experimental_capabilities={}
),
)
await app.run(read_stream, write_stream, init_options)
def run_gradio_server(server_host: str = "0.0.0.0", server_port: int = 7860, limit: int = 10):
"""Run Gradio HTTP MCP server."""
try:
from .gradio_server import launch_gradio_mcp
logger.info(f"Starting Gradio HTTP MCP server on {server_host}:{server_port}...")
launch_gradio_mcp(host=server_host, port=server_port, rate_limit=limit)
except ImportError as exc:
logger.error("Gradio not installed. Install with: pip install mt5-mcp[ui]")
raise RuntimeError(
"Gradio MCP server requires gradio[mcp] package. "
"Install with: pip install mt5-mcp[ui]"
) from exc
def main():
"""Main entry point with multi-transport support."""
parser = argparse.ArgumentParser(
description="MT5 MCP Server v0.5.2 - Fixed MCP Tool Exposure",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Default behavior (run only stdio like previous version)
python -m mt5_mcp
# Run both transports
python -m mt5_mcp --transport both
# Run only streamable HTTP
python -m mt5_mcp --transport http --port 7860
# Customize HTTP rate limit
python -m mt5_mcp --transport http --rate-limit 30
# Disable HTTP rate limiting (not recommended for public servers)
python -m mt5_mcp --transport http --rate-limit 0
MCP Endpoints:
- stdio: Standard input/output (for Claude Desktop, VS Code)
- HTTP: http://localhost:7860/gradio_api/mcp/ (for web-based MCP clients)
""",
)
parser.add_argument(
"--transport",
choices=["stdio", "http", "both"],
default="stdio",
help=(
"Transport method (default: stdio - matches previous releases). "
"Use 'both' for dual-mode or 'http' for HTTP-only"
),
)
parser.add_argument(
"--host",
default="0.0.0.0",
help="Host for HTTP server (default: 0.0.0.0)",
)
parser.add_argument(
"--port",
type=int,
default=7860,
help="Port for HTTP server (default: 7860)",
)
parser.add_argument(
"--rate-limit",
type=int,
default=10,
help="HTTP rate limit (requests per minute per IP, 0=unlimited, default: 10)",
)
parser.add_argument(
"--log-level",
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
default="INFO",
help="Logging level (default: INFO)",
)
parser.add_argument(
"--log-file",
help="Log file path (default: stdout only)",
)
args = parser.parse_args()
# Configure logging
log_handlers = [logging.StreamHandler()]
if args.log_file:
log_handlers.append(logging.FileHandler(args.log_file))
logging.basicConfig(
level=getattr(logging, args.log_level),
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=log_handlers,
)
logger.info(f"MT5 MCP Server v0.5.2 starting with transport: {args.transport}")
# Run appropriate transport(s)
if args.transport == "stdio":
# Run stdio only
asyncio.run(run_stdio_server())
elif args.transport == "http":
# Run HTTP only
run_gradio_server(
server_host=args.host,
server_port=args.port,
limit=args.rate_limit,
)
elif args.transport == "both":
# Run both transports simultaneously
logger.info("Running both stdio and HTTP transports")
logger.info("stdio: Available on standard input/output (unlimited)")
rate_limit_desc = (
f"{args.rate_limit} req/min per IP" if args.rate_limit > 0 else "unlimited"
)
logger.info(
"HTTP: http://%s:%s/gradio_api/mcp/ (rate limit: %s)",
args.host,
args.port,
rate_limit_desc,
)
# Run stdio in background thread
stdio_thread = Thread(
target=lambda: asyncio.run(run_stdio_server()),
daemon=True,
name="stdio-server",
)
stdio_thread.start()
logger.info("stdio server started in background thread")
# Run Gradio in main thread (blocking)
try:
run_gradio_server(
server_host=args.host,
server_port=args.port,
limit=args.rate_limit,
)
except KeyboardInterrupt:
logger.info("Shutting down servers...")
except Exception as e:
logger.error(f"Server error: {e}", exc_info=True)
raise
if __name__ == "__main__":
main()