|
4 | 4 | from struct_module.mcp_server import StructMCPServer |
5 | 5 |
|
6 | 6 |
|
7 | | -# MCP command class for starting the MCP server |
| 7 | +# MCP command class for starting the MCP server (FastMCP stdio only) |
8 | 8 | class MCPCommand(Command): |
9 | 9 | def __init__(self, parser): |
10 | 10 | super().__init__(parser) |
11 | | - parser.description = "MCP (Model Context Protocol) support for struct tool" |
| 11 | + parser.description = "MCP (Model Context Protocol) using FastMCP transports (stdio, http, sse)" |
12 | 12 | parser.add_argument('--server', action='store_true', |
13 | | - help='Start the MCP server for stdio communication') |
| 13 | + help='Start the MCP server') |
| 14 | + parser.add_argument('--transport', choices=['stdio', 'http', 'sse'], default='stdio', |
| 15 | + help='Transport protocol for the MCP server (default: stdio)') |
| 16 | + # HTTP/SSE options |
| 17 | + parser.add_argument('--host', type=str, default='127.0.0.1', help='Host to bind for HTTP/SSE transports') |
| 18 | + parser.add_argument('--port', type=int, default=8000, help='Port to bind for HTTP/SSE transports') |
| 19 | + parser.add_argument('--path', type=str, default='/mcp', help='Endpoint path for HTTP/SSE transports') |
| 20 | + parser.add_argument('--uvicorn-log-level', dest='uvicorn_log_level', type=str, default=None, |
| 21 | + help='Log level for the HTTP server (e.g., info, warning, error)') |
| 22 | + parser.add_argument('--stateless-http', action='store_true', default=None, |
| 23 | + help='Use stateless HTTP mode (HTTP transport only)') |
| 24 | + parser.add_argument('--no-banner', dest='show_banner', action='store_false', default=True, |
| 25 | + help='Disable FastMCP startup banner') |
| 26 | + # Debugging options |
| 27 | + parser.add_argument('--debug', action='store_true', help='Enable debug mode (sets struct and FastMCP loggers to DEBUG by default)') |
| 28 | + parser.add_argument('--fastmcp-log-level', dest='fastmcp_log_level', type=str, default=None, |
| 29 | + help='Log level for FastMCP internals (e.g., DEBUG, INFO). Overrides --debug for FastMCP if provided') |
14 | 30 | parser.set_defaults(func=self.execute) |
15 | 31 |
|
16 | 32 | def execute(self, args): |
17 | 33 | if args.server: |
18 | | - self.logger.info("Starting MCP server for struct tool") |
19 | | - asyncio.run(self._start_mcp_server()) |
| 34 | + self.logger.info( |
| 35 | + f"Starting FastMCP server for struct tool (transport={args.transport})" |
| 36 | + ) |
| 37 | + asyncio.run(self._start_mcp_server(args)) |
20 | 38 | else: |
21 | | - print("MCP (Model Context Protocol) support for struct tool") |
| 39 | + print("MCP (Model Context Protocol) support for struct tool (FastMCP)") |
22 | 40 | print("\nAvailable options:") |
23 | | - print(" --server Start the MCP server for stdio communication") |
| 41 | + print(" --server Start the MCP server") |
| 42 | + print(" --transport {stdio|http|sse} Transport protocol (default: stdio)") |
| 43 | + print(" --host HOST Host for HTTP/SSE (default: 127.0.0.1)") |
| 44 | + print(" --port PORT Port for HTTP/SSE (default: 8000)") |
| 45 | + print(" --path /PATH Endpoint path for HTTP/SSE (default: /mcp)") |
| 46 | + print(" --stateless-http Enable stateless HTTP mode (HTTP only)") |
| 47 | + print(" --no-banner Disable FastMCP banner") |
| 48 | + print(" --debug Enable debug mode (struct + FastMCP DEBUG; uvicorn=debug)") |
| 49 | + print(" --fastmcp-log-level LVL Set FastMCP logger level (overrides --debug for FastMCP)") |
24 | 50 | print("\nMCP tools available:") |
25 | 51 | print(" - list_structures: List all available structure definitions") |
26 | 52 | print(" - get_structure_info: Get detailed information about a structure") |
27 | 53 | print(" - generate_structure: Generate structures with various options") |
28 | 54 | print(" - validate_structure: Validate structure configuration files") |
29 | | - print("\nTo integrate with MCP clients, use: struct mcp --server") |
| 55 | + print("\nExamples:") |
| 56 | + print(" struct mcp --server --transport stdio --debug") |
| 57 | + print(" struct mcp --server --transport http --host 127.0.0.1 --port 9000 --path /mcp --uvicorn-log-level debug") |
| 58 | + print(" struct mcp --server --transport sse --host 0.0.0.0 --port 8080 --path /events --fastmcp-log-level DEBUG") |
30 | 59 |
|
31 | | - async def _start_mcp_server(self): |
32 | | - """Start the MCP server.""" |
| 60 | + async def _start_mcp_server(self, args=None): |
| 61 | + """Start the MCP server using the selected transport.""" |
33 | 62 | try: |
34 | 63 | server = StructMCPServer() |
35 | | - await server.run() |
| 64 | + transport = getattr(args, 'transport', 'stdio') if args else 'stdio' |
| 65 | + # Map CLI args to server.run kwargs |
| 66 | + run_kwargs = { |
| 67 | + "transport": transport, |
| 68 | + "show_banner": getattr(args, 'show_banner', True) if args else True, |
| 69 | + } |
| 70 | + # Determine FastMCP logger level |
| 71 | + fastmcp_log_level = None |
| 72 | + if args: |
| 73 | + fastmcp_log_level = getattr(args, 'fastmcp_log_level', None) |
| 74 | + if not fastmcp_log_level and getattr(args, 'debug', False): |
| 75 | + fastmcp_log_level = 'DEBUG' |
| 76 | + if fastmcp_log_level: |
| 77 | + run_kwargs["fastmcp_log_level"] = fastmcp_log_level |
| 78 | + |
| 79 | + if transport in {"http", "sse"}: |
| 80 | + # uvicorn expects lowercase levels like "info"/"debug" |
| 81 | + uvicorn_level = None |
| 82 | + if args: |
| 83 | + uvicorn_level = getattr(args, 'uvicorn_log_level', None) |
| 84 | + if not uvicorn_level and getattr(args, 'debug', False): |
| 85 | + uvicorn_level = 'debug' |
| 86 | + if not uvicorn_level: |
| 87 | + # Default to args.log if provided, else None |
| 88 | + uvicorn_level = getattr(args, 'log', None) |
| 89 | + run_kwargs.update({ |
| 90 | + "host": getattr(args, 'host', None), |
| 91 | + "port": getattr(args, 'port', None), |
| 92 | + "path": getattr(args, 'path', None), |
| 93 | + "log_level": (uvicorn_level.lower() if isinstance(uvicorn_level, str) else uvicorn_level), |
| 94 | + }) |
| 95 | + if transport == "http": |
| 96 | + run_kwargs["stateless_http"] = getattr(args, 'stateless_http', None) |
| 97 | + await server.run(**run_kwargs) |
36 | 98 | except Exception as e: |
37 | 99 | self.logger.error(f"Error starting MCP server: {e}") |
38 | 100 | raise |
0 commit comments